//draws the isotope label at ten-o-clock private LabelMetrics DrawIsotopeLabel(DrawingContext drawingContext, AtomTextMetrics mainAtomMetrics, AtomTextMetrics hMetrics) { Debug.Assert(Isotope != null); string isoLabel = Isotope.ToString(); var isotopeText = new IsotopeLabelText(isoLabel, PixelsPerDip()); Vector isotopeOffsetVector = BasicGeometry.ScreenNorth * GlyphText.SymbolSize; Matrix rotator = new Matrix(); double angle = -60; //avoid overlap of label and hydrogens if (hMetrics != null && ParentAtom.GetDefaultHOrientation() == CompassPoints.West) { angle = -35; } rotator.Rotate(angle); isotopeOffsetVector = isotopeOffsetVector * rotator; Point isoCenter = mainAtomMetrics.Geocenter + isotopeOffsetVector; isotopeText.MeasureAtCenter(isoCenter); isotopeText.Fill = Fill; isotopeText.DrawAtBottomLeft(isotopeText.TextMetrics.BoundingBox.BottomLeft, drawingContext); return(isotopeText.TextMetrics); }
/// <summary> /// Draws the subscripted group text /// </summary> /// <param name="drawingContext">DC supplied by OnRender</param> /// <param name="measure">Provided by calling the Measure method previously</param> /// <param name="pixelsPerDip"></param> /// <param name="fill"></param> public void DrawSelf(DrawingContext drawingContext, AtomTextMetrics measure, float pixelsPerDip, Brush fill) { _mainText.Fill = fill; _mainText.DrawAtBottomLeft(measure.BoundingBox.BottomLeft, drawingContext); if (_subText != null) { _subText.Fill = fill; _subText.DrawAtBottomLeft(_subText.TextMetrics.BoundingBox.BottomLeft, drawingContext); } }
/// <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); }
public void MeasureAtBottomLeft(Point bottomLeft, float PixelsPerDip) { GlyphInfo = GlyphUtils.GetGlyphsAndInfo(Text, PixelsPerDip, out GlyphRun groupGlyphRun, bottomLeft, _glyphTypeface, TypeSize); TextRun = groupGlyphRun; TextMetrics = new AtomTextMetrics { BoundingBox = groupGlyphRun.GetBoundingBox(bottomLeft), Geocenter = bottomLeft, TotalBoundingBox = groupGlyphRun.GetBoundingBox(bottomLeft), FlattenedPath = GlyphUtils.GetOutline(TextRun), OffsetVector = new Vector(0.0d, 0.0d) }; }
/// <param name="drawingContext"></param> /// <param name="mainAtomMetrics"> /// </param> /// <param name="hMetrics"> /// </param> /// <param name="isoMetrics"> /// </param> /// <param name="chargeString"></param> /// <param name="fill"></param> /// <param name="defaultHOrientation"></param> /// <returns></returns> /// <summary> /// Draws a charge or radical label at the given point /// </summary> /// <returns></returns> private ChargeLabelText DrawChargeOrRadical(DrawingContext drawingContext, AtomTextMetrics mainAtomMetrics, AtomTextMetrics hMetrics, LabelMetrics isoMetrics, string chargeString, Brush fill, CompassPoints defaultHOrientation) { ChargeLabelText chargeText = new ChargeLabelText(chargeString, PixelsPerDip()); //try to place the charge at 2 o clock to the atom Vector chargeOffset = BasicGeometry.ScreenNorth * GlyphText.SymbolSize * 0.9; RotateUntilClear(mainAtomMetrics, hMetrics, isoMetrics, chargeOffset, chargeText, out var chargeCenter, defaultHOrientation); chargeText.MeasureAtCenter(chargeCenter); chargeText.Fill = fill; chargeText.DrawAtBottomLeft(chargeText.TextMetrics.BoundingBox.BottomLeft, drawingContext); return(chargeText); }
/// <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); }
private static void RotateUntilClear(AtomTextMetrics mainAtomMetrics, AtomTextMetrics hMetrics, LabelMetrics isoMetrics, Vector labelOffset, GlyphText labelText, out Point labelCenter, CompassPoints defHOrientation) { Matrix rotator = new Matrix(); double angle = Globals.ClockDirections.II.ToDegrees(); rotator.Rotate(angle); labelOffset = labelOffset * rotator; Rect bb = new Rect(); Rect bb2 = new Rect(); if (hMetrics != null) { bb = hMetrics.TotalBoundingBox; } if (isoMetrics != null) { bb2 = isoMetrics.BoundingBox; } labelCenter = mainAtomMetrics.Geocenter + labelOffset; labelText.MeasureAtCenter(labelCenter); double increment; if (defHOrientation == CompassPoints.East) { increment = -10; } else { increment = 10; } while (labelText.CollidesWith(mainAtomMetrics.TotalBoundingBox, bb, bb2) & Math.Abs(angle - 30) > 0.001) { rotator = new Matrix(); angle += increment; rotator.Rotate(increment); labelOffset = labelOffset * rotator; labelCenter = mainAtomMetrics.Geocenter + labelOffset; labelText.MeasureAtCenter(labelCenter); } }
public void MeasureAtCenter(Point center) { GlyphInfo = GlyphUtils.GetGlyphsAndInfo(Text, PixelsPerDip, out GlyphRun groupGlyphRun, center, _glyphTypeface, TypeSize); //compensate the main offset vector for any descenders Vector mainOffset = GlyphUtils.GetOffsetVector(groupGlyphRun, TypeSize) + new Vector(0.0, -MaxBaselineOffset); var bb = groupGlyphRun.GetBoundingBox(center + mainOffset); Vector textFormatterOffset = new Vector(mainOffset.X, -FirstBearing(groupGlyphRun) - bb.Height / 2); TextRun = groupGlyphRun; TextMetrics = new AtomTextMetrics { BoundingBox = bb, Geocenter = center, TotalBoundingBox = groupGlyphRun.GetBoundingBox(center + mainOffset), OffsetVector = mainOffset, TextFormatterOffset = textFormatterOffset }; }
/// <summary> /// Gets the center point of an atom adjunct (like an implicit hydrogen plus subscripts) /// The Adjunct in NH2 is H2 /// </summary> /// <param name="parentMetrics">Metrics of the parent atom</param> /// <param name="direction">NESW direction of the adjunct respective to the atom</param> /// <param name="adjunctGlyphInfo">Initial measurements of the adjunct</param> /// <param name="subscriptInfo">Initial measurements of the subscript (can be null for no subscripts)</param> /// <returns></returns> private static Point GetAdjunctCenter(AtomTextMetrics parentMetrics, CompassPoints direction, GlyphInfo adjunctGlyphInfo, GlyphInfo?subscriptInfo = null) { Point adjunctCenter; double charHeight = (GlyphUtils.GlyphTypeface.Baseline * _fontSize); double adjunctWidth = (parentMetrics.BoundingBox.Width + adjunctGlyphInfo.Width) / 2; switch (direction) { //all addition in this routine is *vector* addition. //We are not adding absolute X and Y values case CompassPoints.East: default: adjunctCenter = parentMetrics.Geocenter + BasicGeometry.ScreenEast * adjunctWidth; break; case CompassPoints.North: adjunctCenter = parentMetrics.Geocenter + BasicGeometry.ScreenNorth * charHeight; break; case CompassPoints.West: if (subscriptInfo != null) { adjunctCenter = parentMetrics.Geocenter + (BasicGeometry.ScreenWest * (adjunctWidth + subscriptInfo.Value.Width)); } else { adjunctCenter = parentMetrics.Geocenter + (BasicGeometry.ScreenWest * (adjunctWidth)); } break; case CompassPoints.South: adjunctCenter = parentMetrics.Geocenter + BasicGeometry.ScreenSouth * charHeight; break; } return(adjunctCenter); }
private void RenderAtom(DrawingContext drawingContext) { //renders the atom complete with charges, hydrogens and labels. //this code is *complex* - alter it at your own risk! List <Point> symbolPoints = new List <Point>(); List <Point> hydrogenPoints = new List <Point>(); //private variables used to keep track of onscreen visuals AtomTextMetrics hydrogenMetrics = null; LabelMetrics isoMetrics = null; SubscriptedGroup subscriptedGroup = null; Hull = new List <Point>(); //stage 1: measure up the main atom symbol in position //we need the metrics first if (AtomSymbol != "") { var symbolText = new GlyphText(AtomSymbol, SymbolTypeface, GlyphText.SymbolSize, PixelsPerDip()); symbolText.MeasureAtCenter(Position); //grab the hull for later if (symbolText.FlattenedPath != null) { Hull.AddRange(symbolText.FlattenedPath); } } //stage 2. grab the main atom metrics br drawing it var mainAtomMetrics = DrawSelf(drawingContext); //if it's a vertex atom we need the hull if (AtomSymbol == "") { Hull.AddRange(mainAtomMetrics.FlattenedPath); } //stage 3: measure up the hydrogens //if we have implicit hydrogens and we have an explicit label, draw them if (ImplicitHydrogenCount > 0 && AtomSymbol != "") { var defaultHOrientation = ParentAtom.GetDefaultHOrientation(); subscriptedGroup = new SubscriptedGroup(ImplicitHydrogenCount, "H", GlyphText.SymbolSize); hydrogenMetrics = subscriptedGroup.Measure(mainAtomMetrics, defaultHOrientation, PixelsPerDip()); subscriptedGroup.DrawSelf(drawingContext, hydrogenMetrics, PixelsPerDip(), Fill); hydrogenPoints = hydrogenMetrics.FlattenedPath; Hull.AddRange(hydrogenPoints); } //stage 6: draw an isotope label if needed if (Isotope != null) { isoMetrics = DrawIsotopeLabel(drawingContext, mainAtomMetrics, hydrogenMetrics); Hull.AddRange(isoMetrics.Corners); } //stage7: draw any charges if ((Charge ?? 0) != 0) { LabelMetrics cMetrics = DrawCharges(drawingContext, mainAtomMetrics, hydrogenMetrics, isoMetrics, ParentAtom.GetDefaultHOrientation()); Hull.AddRange(cMetrics.FlattenedPath); } //stage 8: recalculate the hull if (Hull.Any()) { //sort the points properly before doing a hull calculation var sortedHull = (from Point p in Hull orderby p.X, p.Y descending select p).ToList(); Hull = Geometry <Point> .GetHull(sortedHull, p => p); // Diag: Show the Hull #if DEBUG #if SHOWHULLS ShowHull(Hull, drawingContext); #endif #endif // End Diag } // Diag: Show the Atom Point #if DEBUG #if SHOWATOMCENTRES drawingContext.DrawEllipse(Brushes.Red, null, ParentAtom.Position, 5, 5); #endif #endif // End Diag }