Esempio n. 1
0
        /// <inheritdoc/>
        public IRenderingElement Generate(IReaction reaction, RendererModel model)
        {
            if (!model.GetShowReactionBoxes())
            {
                return(null);
            }
            if (reaction.Reactants.Count == 0)
            {
                return(new ElementGroup());
            }

            double separation  = model.GetBondLength() / model.GetScale() / 2;
            var    totalBounds = BoundsCalculator.CalculateBounds(reaction.Reactants);

            ElementGroup diagram         = new ElementGroup();
            double       minX            = totalBounds.Left;
            double       minY            = totalBounds.Top;
            double       maxX            = totalBounds.Right;
            double       maxY            = totalBounds.Bottom;
            var          foregroundColor = model.GetForegroundColor();

            diagram.Add(new RectangleElement(new Rect(minX - separation, minY - separation, maxX + separation, maxY + separation), foregroundColor));
            diagram.Add(new TextElement(new Point((minX + maxX) / 2, minY - separation), "Reactants", foregroundColor));
            return(diagram);
        }
Esempio n. 2
0
        /// <inheritdoc/>
        public IRenderingElement Generate(IReaction reaction, RendererModel model)
        {
            if (!model.GetShowReactionBoxes())
            {
                return(null);
            }
            var separation  = model.GetBondLength() / model.GetScale();
            var totalBounds = BoundsCalculator.CalculateBounds(reaction);

            if (totalBounds.IsEmpty)
            {
                return(null);
            }

            var diagram         = new ElementGroup();
            var foregroundColor = model.GetForegroundColor();

            diagram.Add(new RectangleElement(new Rect(totalBounds.Left - separation, totalBounds.Top - separation,
                                                      totalBounds.Right + separation, totalBounds.Bottom + separation),
                                             foregroundColor));
            if (reaction.Id != null)
            {
                diagram.Add(new TextElement(new Point((totalBounds.Left + totalBounds.Right) / 2, totalBounds.Top - separation),
                                            reaction.Id, foregroundColor));
            }
            return(diagram);
        }
Esempio n. 3
0
        private StandardSgroupGenerator(RendererModel parameters, StandardAtomGenerator atomGenerator, double stroke, Typeface font, double emSize, Color foreground)
        {
            this.font   = font;
            this.emSize = emSize;
            this.scale  = parameters.GetScale();
            this.stroke = stroke;
            double length = parameters.GetBondLength() / scale;

            this.bracketDepth = parameters.GetSgroupBracketDepth() * length;
            this.labelScale   = parameters.GetSgroupFontScale();

            // foreground is based on the carbon color
            this.foreground    = foreground;
            this.atomGenerator = atomGenerator;
            this.parameters    = parameters;
        }
Esempio n. 4
0
        /// <inheritdoc/>
        public IRenderingElement Generate(IReaction reaction, RendererModel model)
        {
            if (!model.GetShowReactionBoxes())
            {
                return(null);
            }
            if (reaction.Products.Count == 0)
            {
                return(new ElementGroup());
            }
            double distance    = model.GetBondLength() / model.GetScale() / 2;
            Rect?  totalBounds = null;

            foreach (var molecule in reaction.Products)
            {
                var bounds = BoundsCalculator.CalculateBounds(molecule);
                if (totalBounds == null)
                {
                    totalBounds = bounds;
                }
                else
                {
                    totalBounds = Rect.Union(totalBounds.Value, bounds);
                }
            }
            if (totalBounds == null)
            {
                return(null);
            }

            ElementGroup diagram         = new ElementGroup();
            var          foregroundColor = model.GetForegroundColor();

            diagram.Add(new RectangleElement(
                            new Rect(
                                totalBounds.Value.Left - distance,
                                totalBounds.Value.Top - distance,
                                totalBounds.Value.Right + distance,
                                totalBounds.Value.Bottom + distance),
                            foregroundColor));
            diagram.Add(new TextElement(
                            new Point(
                                (totalBounds.Value.Left + totalBounds.Value.Right) / 2,
                                totalBounds.Value.Top - distance),
                            "Products", foregroundColor));
            return(diagram);
        }
Esempio n. 5
0
        /// <inheritdoc/>
        public IRenderingElement Generate(IReaction reaction, RendererModel model)
        {
            var totalBoundsReactants = BoundsCalculator.CalculateBounds(reaction.Reactants);
            var totalBoundsProducts  = BoundsCalculator.CalculateBounds(reaction.Products);

            if (totalBoundsReactants == null || totalBoundsProducts == null)
            {
                return(null);
            }

            double separation      = model.GetBondLength() / model.GetScale();
            var    foregroundColor = model.GetForegroundColor();

            return(new ArrowElement(
                       new WPF::Point(totalBoundsReactants.Right + separation, totalBoundsReactants.CenterY()),
                       new WPF::Point(totalBoundsProducts.Left - separation, totalBoundsReactants.CenterY()),
                       1 / model.GetScale(), true, foregroundColor));
        }
Esempio n. 6
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);
        }
Esempio n. 7
0
 /// <summary>
 /// Utility method for recalling a depiction in pixels to one in millimeters.
 /// </summary>
 /// <param name="bondLength">the desired bond length (mm)</param>
 /// <returns>the scaling factor</returns>
 internal double RescaleForBondLength(double bondLength)
 {
     return(bondLength / model.GetBondLength());
 }