Пример #1
0
        private static TextOutline LeftAlign(TextOutline outline)
        {
            var center = outline.GetCenter();
            var first  = outline.GetFirstGlyphCenter();

            return(outline.Translate(center.X - first.X, center.Y - first.Y));
        }
Пример #2
0
        /// <summary>
        /// Generate an annotation 'label' for an atom (located at 'basePoint'). The label is offset from
        /// the basePoint by the provided 'distance' and 'direction'.
        /// </summary>
        /// <param name="basePoint">the relative (0,0) reference</param>
        /// <param name="label">the annotation text</param>
        /// <param name="direction">the direction along which the label is laid out</param>
        /// <param name="distance">the distance along the direct to travel</param>
        /// <param name="scale">the font scale of the label</param>
        /// <param name="font">the font to use</param>
        /// <param name="symbol">the atom symbol to avoid overlap with</param>
        /// <returns>the position text outline for the annotation</returns>
        internal static TextOutline GenerateAnnotation(Vector2 basePoint, string label, Vector2 direction, double distance, double scale, Typeface font, double emSize, AtomSymbol symbol)
        {
            var italicHint = label.StartsWith(ItalicDisplayPrefix);

            label = italicHint ? label.Substring(ItalicDisplayPrefix.Length) : label;
            var annFont    = italicHint ? new Typeface(font.FontFamily, WPF.FontStyles.Italic, font.Weight, font.Stretch) : font;
            var annOutline = new TextOutline(label, annFont, emSize).Resize(scale, -scale);

            // align to the first or last character of the annotation depending on the direction
            var center = direction.X > 0.3 ? annOutline.GetFirstGlyphCenter() : direction.X < -0.3 ? annOutline.GetLastGlyphCenter() : annOutline.GetCenter();

            // Avoid atom symbol
            if (symbol != null)
            {
                var intersect = symbol.GetConvexHull().Intersect(VecmathUtil.ToPoint(basePoint), VecmathUtil.ToPoint(VecmathUtil.Sum(basePoint, direction)));
                // intersect should never be null be check against this
                if (intersect != null)
                {
                    basePoint = VecmathUtil.ToVector(intersect);
                }
            }

            direction *= distance;
            direction += basePoint;

            // move to position
            return(annOutline.Translate(direction.X - center.X, direction.Y - center.Y));
        }
Пример #3
0
        public TextOutline PositionAfter(TextOutline before, TextOutline after)
        {
            var fixedBounds   = before.GetBounds();
            var movableBounds = after.GetBounds();

            after = after.Translate((fixedBounds.Right + padding) - movableBounds.Left, 0);
            return(after);
        }
Пример #4
0
        /// <summary>
        /// Position the mass label relative to the element label. The mass adjunct is position to the
        /// top left of the element label.
        /// </summary>
        /// <param name="massLabel">mass label outline</param>
        /// <param name="elementLabel">element label outline</param>
        /// <returns>positioned mass label</returns>
        public TextOutline PositionMassLabel(TextOutline massLabel, TextOutline elementLabel)
        {
            var elementBounds = elementLabel.GetBounds();
            var massBounds    = massLabel.GetBounds();

            return(massLabel.Translate((elementBounds.Left - padding) - massBounds.Right,
                                       (elementBounds.Top - (massBounds.Height / 2)) - massBounds.Top));
        }
Пример #5
0
        public TextOutline PositionSuperscript(TextOutline label, TextOutline superscript)
        {
            var labelBounds       = label.GetBounds();
            var superscriptBounds = superscript.GetBounds();

            superscript = superscript.Translate((labelBounds.Right + padding) - superscriptBounds.Left,
                                                (labelBounds.Top - (superscriptBounds.Height / 2)) - superscriptBounds.Top);
            return(superscript);
        }
Пример #6
0
        /// <summary>
        /// Positions an outline in the subscript position relative to another 'primary' label.
        /// </summary>
        /// <param name="label">a label outline</param>
        /// <param name="subscript">the label outline to position as subscript</param>
        /// <returns>positioned subscript outline</returns>
        public TextOutline PositionSubscript(TextOutline label, TextOutline subscript)
        {
            var hydrogenBounds      = label.GetBounds();
            var hydrogenCountBounds = subscript.GetBounds();

            subscript = subscript.Translate((hydrogenBounds.Right + padding) - hydrogenCountBounds.Left,
                                            (hydrogenBounds.Bottom + (hydrogenCountBounds.Height / 2)) - hydrogenCountBounds.Bottom);
            return(subscript);
        }
Пример #7
0
        public void TransformedCenter()
        {
            TextOutline original    = new TextOutline("Cl", font, emSize);
            TextOutline transformed = original.Translate(0, -5);
            var         oCenter     = original.GetCenter();
            var         tCenter     = transformed.GetCenter();

            Assert.AreEqual(oCenter.X, tCenter.X, 0.01);
            Assert.AreEqual(oCenter.Y - 5, tCenter.Y, 0.01);
        }
Пример #8
0
        public void BoundsTransformedWithYTranslation()
        {
            TextOutline original    = new TextOutline("Cl", font, emSize);
            TextOutline transformed = original.Translate(0, -5);
            var         oBounds     = original.GetBounds();
            var         tBounds     = transformed.GetBounds();

            Assert.AreEqual(oBounds.X, tBounds.X, 0.01);
            Assert.AreEqual(oBounds.Y - 5, tBounds.Y, 0.01);
            Assert.AreEqual(oBounds.Width, tBounds.Width, 0.01);
            Assert.AreEqual(oBounds.Height, tBounds.Height, 0.01);
        }
Пример #9
0
        /// <summary>
        /// Position the hydrogen label relative to the element label.
        /// </summary>
        /// <param name="position">relative position where the hydrogen is placed</param>
        /// <param name="element">the outline of the element label</param>
        /// <param name="hydrogen">the outline of the hydrogen</param>
        /// <returns>positioned hydrogen label</returns>
        public TextOutline PositionHydrogenLabel(HydrogenPosition position, TextOutline element, TextOutline hydrogen)
        {
            var elementBounds  = element.GetBounds();
            var hydrogenBounds = hydrogen.GetBounds();

            switch (position)
            {
            case HydrogenPosition.Above:
                return(hydrogen.Translate(0, (elementBounds.Top - padding) - hydrogenBounds.Bottom));

            case HydrogenPosition.Right:
                return(hydrogen.Translate((elementBounds.Right + padding) - hydrogenBounds.Left, 0));

            case HydrogenPosition.Below:
                return(hydrogen.Translate(0, (elementBounds.Bottom + padding) - hydrogenBounds.Top));

            case HydrogenPosition.Left:
                return(hydrogen.Translate((elementBounds.Left - padding) - hydrogenBounds.Right, 0));
            }
            return(hydrogen); // never reached
        }
Пример #10
0
        /// <summary>
        /// Generate an atom symbol for a periodic element with the specified number of hydrogens, ionic
        /// charge, mass,
        /// </summary>
        /// <param name="number">atomic number</param>
        /// <param name="hydrogens">labelled hydrogen count</param>
        /// <param name="mass">atomic mass</param>
        /// <param name="charge">ionic formal charge</param>
        /// <param name="unpaired">number of unpaired electrons</param>
        /// <param name="position">placement of hydrogen</param>
        /// <returns>laid out atom symbol</returns>
        public AtomSymbol GeneratePeriodicSymbol(int number, int hydrogens, int mass, int charge, int unpaired, HydrogenPosition position)
        {
            var element = number == 0
                        ? new TextOutline("*", font, emSize)
                        : new TextOutline(ChemicalElement.Of(number).Symbol, font, emSize);
            var hydrogenAdjunct = defaultHydrogenLabel;

            // the hydrogen count, charge, and mass adjuncts are script size
            var hydrogenCount = new TextOutline(hydrogens.ToString(), font, emSize).Resize(scriptSize, scriptSize);
            var chargeAdjunct = new TextOutline(ChargeAdjunctText(charge, unpaired), font, emSize).Resize(scriptSize, scriptSize);
            var massAdjunct   = new TextOutline(mass.ToString(), font, emSize).Resize(scriptSize, scriptSize);

            // position each adjunct relative to the element label and each other
            hydrogenAdjunct = PositionHydrogenLabel(position, element, hydrogenAdjunct);
            hydrogenCount   = PositionSubscript(hydrogenAdjunct, hydrogenCount);
            chargeAdjunct   = PositionChargeLabel(hydrogens, position, chargeAdjunct, element, hydrogenAdjunct);
            massAdjunct     = PositionMassLabel(massAdjunct, element);

            // when the hydrogen label is positioned to the left we may need to nudge it
            // over to account for the hydrogen count and/or the mass adjunct colliding
            // with the element label
            if (position == HydrogenPosition.Left)
            {
                var nudgeX = HydrogenXDodge(hydrogens, mass, element, hydrogenAdjunct, hydrogenCount, massAdjunct);
                hydrogenAdjunct = hydrogenAdjunct.Translate(nudgeX, 0);
                hydrogenCount   = hydrogenCount.Translate(nudgeX, 0);
            }

            var adjuncts = new List <TextOutline>(4);

            if (hydrogens > 0)
            {
                adjuncts.Add(hydrogenAdjunct);
            }
            if (hydrogens > 1)
            {
                adjuncts.Add(hydrogenCount);
            }
            if (charge != 0 || unpaired > 0)
            {
                adjuncts.Add(chargeAdjunct);
            }
            if (mass > 0)
            {
                adjuncts.Add(massAdjunct);
            }

            return(new AtomSymbol(element, adjuncts));
        }
Пример #11
0
        public void PositionOfChargeWhenHydrogensAreRight()
        {
            // hydrogen is arbitrarily moved to ensure x/y are different from the element
            var charge        = new TextOutline("+", font, emSize);
            var localHydrogen = hydrogen.Translate(10, 10);
            var positioned    = atomGenerator.PositionChargeLabel(1, HydrogenPosition.Right, charge, element,
                                                                  localHydrogen);

            var hydrogenBounds = localHydrogen.GetBounds();
            var chargeBounds   = positioned.GetBounds();

            Assert.IsTrue(chargeBounds.Left > hydrogenBounds.Left);
            Assert.AreEqual(hydrogenBounds.Top, chargeBounds.CenterY(), 0.01);
        }
Пример #12
0
        /// <summary>
        /// Position the charge label on the top right of either the element or hydrogen label. Where the
        /// charge is placed depends on the number of hydrogens and their position relative to the
        /// element symbol.
        /// </summary>
        /// <param name="hydrogens">number of hydrogen</param>
        /// <param name="position">position of hydrogen</param>
        /// <param name="charge">the charge label outline (to be positioned)</param>
        /// <param name="element">the element label outline</param>
        /// <param name="hydrogen">the hydrogen label outline</param>
        /// <returns>positioned charge label</returns>
        public TextOutline PositionChargeLabel(int hydrogens, HydrogenPosition position, TextOutline charge, TextOutline element, TextOutline hydrogen)
        {
            var chargeBounds = charge.GetBounds();

            // the charge is placed to the top right of the element symbol
            // unless either the hydrogen label or the hydrogen count label
            // are in the way - in which case we place it relative to the
            // hydrogen
            var referenceBounds = element.GetBounds();

            if (hydrogens > 0 && position == HydrogenPosition.Right)
            {
                referenceBounds = hydrogen.GetBounds();
            }
            else if (hydrogens > 1 && position == HydrogenPosition.Above)
            {
                referenceBounds = hydrogen.GetBounds();
            }

            return(charge.Translate((referenceBounds.Right + padding) - chargeBounds.Left,
                                    (referenceBounds.Top - (chargeBounds.Height / 2)) - chargeBounds.Top));
        }
Пример #13
0
        /// <summary>
        /// Make an embedded text label for display in a CDK renderer. If a piece of text contains newlines
        /// they are centred aligned below each other with a line height of 1.4.
        /// </summary>
        /// <param name="font">the font to embedded</param>
        /// <param name="text">the text label</param>
        /// <param name="color">the color</param>
        /// <param name="scale">the resize, should include the model scale</param>
        /// <returns>pre-rendered element</returns>
        public static IRenderingElement EmbedText(Typeface font, double emSize, string text, Color color, double scale)
        {
            var lines = text.Split('\n');
            var group = new ElementGroup();

            double yOffset    = 0;
            double lineHeight = 1.4d;

            foreach (var line in lines)
            {
                var outline = new TextOutline(line, font, emSize).Resize(scale, -scale);
                var center  = outline.GetCenter();
                outline = outline.Translate(-center.X, -(center.Y + yOffset));

                yOffset += lineHeight * outline.GetBounds().Height;

                group.Add(GeneralPath.ShapeOf(outline.GetOutline(), color));
                var logicalBounds = outline.LogicalBounds;
                group.Add(new Bounds(logicalBounds.Left, logicalBounds.Top, logicalBounds.Right, logicalBounds.Bottom));
            }

            return(group);
        }
Пример #14
0
        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);
        }
Пример #15
0
        public IRenderingElement Generate()
        {
            if (!delocalisedDonuts)
            {
                return(null);
            }
            var group = new ElementGroup();

            smallest = Cycles.EdgeShort.Find(mol).ToRingSet();
            foreach (var ring in smallest)
            {
                if (!CanDelocalise(ring))
                {
                    continue;
                }
                foreach (var bond in ring.Bonds)
                {
                    bonds.Add(bond);
                }
                int charge   = 0;
                int unpaired = 0;
                foreach (var atom in ring.Atoms)
                {
                    var q = atom.FormalCharge ?? 0;
                    if (q == 0)
                    {
                        continue;
                    }
                    int nCyclic = 0;
                    foreach (var bond in mol.GetConnectedBonds(atom))
                    {
                        if (bond.IsInRing)
                        {
                            nCyclic++;
                        }
                    }
                    if (nCyclic > 2)
                    {
                        continue;
                    }
                    atoms.Add(atom);
                    charge += q;
                }
                var p2 = GeometryUtil.Get2DCenter(ring);

                if (charge != 0)
                {
                    var qText = charge < 0 ? "–" : "+";
                    if (charge < -1)
                    {
                        qText = Math.Abs(charge) + qText;
                    }
                    else if (charge > +1)
                    {
                        qText = Math.Abs(charge) + qText;
                    }

                    TextOutline qSym = new TextOutline(qText, font, emSize);
                    qSym = qSym.Resize(1 / scale, -1 / scale);
                    qSym = qSym.Translate(p2.X - qSym.GetCenter().X,
                                          p2.Y - qSym.GetCenter().Y);
                    group.Add(GeneralPath.ShapeOf(qSym.GetOutline(), fgColor));
                }

                double s = GeometryUtil.GetBondLengthMedian(ring);
                double n = ring.Bonds.Count;
                double r = s / (2 * Math.Tan(Math.PI / n));
                group.Add(new OvalElement(new WPF::Point(p2.X, p2.Y), r - 1.5 * dbSpacing,
                                          stroke, false, fgColor));
            }
            return(group);
        }