Example #1
0
        /// <summary>
        /// Gets or sets the <see cref="CursorToken"/> that corresponds to a particular point on the compass.
        /// </summary>
        /// <param name="compassPoint">the compass point.</param>
        /// <returns>the <see cref="CursorToken"/> that corresponds to the specified point on the compass, or null.</returns>
        private CursorToken this[CompassPoints compassPoint]
        {
            get
            {
                if (!_stretchIndicatorTokens.ContainsKey(compassPoint))
                {
                    return(null);
                }

                return(_stretchIndicatorTokens[compassPoint]);
            }
            set
            {
                if (value != null)
                {
                    _stretchIndicatorTokens[compassPoint] = value;
                }
                else
                {
                    if (_stretchIndicatorTokens.ContainsKey(compassPoint))
                    {
                        _stretchIndicatorTokens.Remove(compassPoint);
                    }
                }
            }
        }
Example #2
0
            public void initialize_rover_with_point_and_direction(int x, int y, CompassPoints direction)
            {
                var point = new CoordinatesPoint(x, y);
                var rover = new Rover(point, direction);

                Assert.AreEqual(rover.GetCoordinatesPoint(), point);
                Assert.AreEqual(rover.GetCompassPoints(), direction);
            }
        /// <summary>
        /// Initializes a new instance of the <see cref="Vehicle"/> class.
        /// </summary>
        public Vehicle()
        {
            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();

            // TODO: Add any initialization after the InitializeComponent call
            this.orientation = CompassPoints.north;
            this.magnitude   = 0;
        }
Example #4
0
        public Port(Id id, CompassPoints? compassPoint)
        {
            if (id == null && compassPoint == null)
            {
                throw new ArgumentException("A port must specify either an ID, a compass point, or both.");
            }

            this.id = id;
            this.compassPoint = compassPoint;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="Vehicle"/> class.
        /// </summary>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="width">The width.</param>
        /// <param name="height">The height.</param>
        public Vehicle(int x, int y, int width, int height)
        {
            this.InitializeComponent();
            this.Location = new System.Drawing.Point(x, y);


            this.Width       = width;
            this.Height      = height;
            this.orientation = CompassPoints.north;
            this.magnitude   = 0;
        }
Example #6
0
        /// <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);
        }
Example #7
0
            /// <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);
            }
Example #8
0
            public void add_rover_to_plateau_get_last_added()
            {
                var plateau = new Plate();

                plateau.SetSize(5, 5);

                const CompassPoints direction = CompassPoints.E;
                var roverPosition             = new CoordinatesPoint(1, 2);
                var rover = new Rover(roverPosition, direction);

                plateau.AddRover(rover);
                var lastRover = plateau.GetLastRover();

                Assert.AreEqual(rover, lastRover);
            }
Example #9
0
        /// <summary>
        /// Computes the position on a given rectangle that corresponds to the given compass point.
        /// </summary>
        /// <param name="compassPoint">the compass point whose position on the rectangle is to be determined.</param>
        /// <param name="rectangle">the rectangle.</param>
        /// <returns>the point on the rectangle that corresponds to the given compass point.</returns>
        private PointF GetCompassPointPosition(CompassPoints compassPoint, RectangleF rectangle)
        {
            float top    = rectangle.Top;
            float left   = rectangle.Left;
            float right  = rectangle.Right;
            float bottom = rectangle.Bottom;

            float centreX = left;

            if (left != right)
            {
                centreX = rectangle.Left + rectangle.Width / 2F;
            }

            float centreY = top;

            if (top != bottom)
            {
                centreY = rectangle.Top + rectangle.Height / 2F;
            }

            switch (compassPoint)
            {
            case CompassPoints.NorthWest:
                return(new PointF(left, top));

            case CompassPoints.NorthEast:
                return(new PointF(right, top));

            case CompassPoints.SouthEast:
                return(new PointF(right, bottom));

            case CompassPoints.SouthWest:
                return(new PointF(left, bottom));

            case CompassPoints.North:
                return(new PointF(centreX, top));

            case CompassPoints.East:
                return(new PointF(right, centreY));

            case CompassPoints.South:
                return(new PointF(centreX, bottom));

            default:                     //CompassPoints.West:
                return(new PointF(left, centreY));
            }
        }
        /// <summary>
        /// Offsets the specified orientation.
        /// </summary>
        /// <param name="orientation">The orientation.</param>
        /// <param name="magnitude">The magnitude.</param>
        public void Offset(CompassPoints orientation, int magnitude)
        {
            this.orientation = orientation;
            this.magnitude   = (double)magnitude;
            switch (orientation)
            {
            case CompassPoints.north:
                this.Location = new Point(this.Location.X, this.Location.Y - (int)this.magnitude);
                break;

            case CompassPoints.northeast:
                this.Location = new Point(this.Location.X + ((int)Math.Round(Math.Sqrt((Math.Pow(this.magnitude, 2)) / 2), 0)), this.Location.Y + (int)-Math.Round((Math.Sqrt((Math.Pow(this.magnitude, 2)) / 2)), 0));
                break;

            case CompassPoints.east:
                this.Location = new Point(this.Location.X + magnitude, this.Location.Y);
                break;

            case CompassPoints.southeast:
                this.Location = new Point(this.Location.X + (int)Math.Round(Math.Sqrt((Math.Pow(this.magnitude, 2)) / 2), 0), this.Location.Y + (int)Math.Round(Math.Sqrt((Math.Pow(this.magnitude, 2) / 2)), 0));

                break;

            case CompassPoints.south:
                this.Location = new Point(this.Location.X, this.Location.Y + (magnitude));

                break;

            case CompassPoints.southwest:
                this.Location = new Point(this.Location.X + (int)-Math.Round(Math.Sqrt(Math.Pow(this.magnitude, 2) / 2), 0), this.Location.Y + (int)Math.Round((Math.Sqrt((Math.Pow(this.magnitude, 2)) / 2)), 0));

                break;

            case CompassPoints.west:
                this.Location = new Point(this.Location.X - (magnitude), this.Location.Y);

                break;

            case CompassPoints.northwest:
                this.Location = new Point(this.Location.X + (int)-Math.Round(Math.Sqrt((Math.Pow(this.magnitude, 2)) / 2), 0), this.Location.Y + (int)-Math.Round(Math.Sqrt((Math.Pow(this.magnitude, 2)) / 2), 0));

                break;

            default:

                break;
            }
        }
Example #11
0
        public InnerWallBoard(GamesBoard gb, int colPosition, int rowPosition, CompassPoints wallSide)
        {
            for (int c = 0; c < Constants.BOARDCOLSIZE; c++)
            {
                for (int r = 0; r < Constants.BOARDROWSIZE; r++)
                {
                    board[c][r].boardSpace = gb.board[c][r].boardSpace;
                    board[c][r].northWall  = gb.board[c][r].northWall;
                    board[c][r].southWall  = gb.board[c][r].southWall;
                    board[c][r].westWall   = gb.board[c][r].westWall;
                    board[c][r].eastWall   = gb.board[c][r].eastWall;
                }
            }

            this.prevDeco = gb;
            switch (wallSide)
            {
            case CompassPoints.NORTH:
                gb.board[colPosition][rowPosition].northWall       = true;
                board[colPosition][rowPosition].northWall          = true;
                prevDeco.board[colPosition][rowPosition].northWall = true;
                break;

            case CompassPoints.SOUTH:
                gb.board[colPosition][rowPosition].southWall       = true;
                board[colPosition][rowPosition].southWall          = true;
                prevDeco.board[colPosition][rowPosition].southWall = true;
                break;

            case CompassPoints.WEST:
                gb.board[colPosition][rowPosition].westWall       = true;
                board[colPosition][rowPosition].westWall          = true;
                prevDeco.board[colPosition][rowPosition].westWall = true;
                break;

            case CompassPoints.EAST:
                gb.board[colPosition][rowPosition].eastWall       = true;
                board[colPosition][rowPosition].eastWall          = true;
                prevDeco.board[colPosition][rowPosition].eastWall = true;
                break;

            default:
                break;
            }
        }
Example #12
0
            /// <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);
            }
Example #13
0
        /// <summary>
        /// Gets the appropriate <see cref="CursorToken"/> for a given point (in destination coordinates).
        /// </summary>
        /// <param name="point">the point (in destination coordinates).</param>
        /// <returns>a <see cref="CursorToken"/> that is appropriate for the given point, or null.</returns>
        public override CursorToken GetCursorToken(Point point)
        {
            if (_stretchIndicatorTokens.Count == 0)
            {
                return(null);
            }

            Platform.CheckForNullReference(ControlPoints, "_controlPoints");

            int controlPointIndex = ControlPoints.HitTestControlPoint(point);

            if (controlPointIndex < 0)
            {
                return(null);
            }

            ControlPoints.CoordinateSystem = CoordinateSystem.Destination;

            PointF     controlPoint        = ControlPoints[controlPointIndex];
            RectangleF containingRectangle = this.BoundingRectangle;

            CompassPoints closestCompassPoint = _stretchIndicatorTokens.Keys[0];
            float         minDistance         = DistanceToCompassPoint(controlPoint, containingRectangle, closestCompassPoint);

            for (int i = 1; i < _stretchIndicatorTokens.Keys.Count; ++i)
            {
                CompassPoints compassPoint = _stretchIndicatorTokens.Keys[i];
                float         distance     = DistanceToCompassPoint(controlPoint, containingRectangle, compassPoint);

                if (distance <= minDistance)
                {
                    closestCompassPoint = compassPoint;
                    minDistance         = distance;
                }
            }

            ControlPoints.ResetCoordinateSystem();

            return(_stretchIndicatorTokens[closestCompassPoint]);
        }
		/// <summary>
		/// Gets or sets the <see cref="CursorToken"/> that corresponds to a particular point on the compass.
		/// </summary>
		/// <param name="compassPoint">the compass point.</param>
		/// <returns>the <see cref="CursorToken"/> that corresponds to the specified point on the compass, or null.</returns>
		private CursorToken this[CompassPoints compassPoint]
		{
			get
			{
				if (!_stretchIndicatorTokens.ContainsKey(compassPoint))
					return null;

				return _stretchIndicatorTokens[compassPoint]; 
			}
			set
			{
				if (value != null)
				{
					_stretchIndicatorTokens[compassPoint] = value;
				}
				else
				{
					if (_stretchIndicatorTokens.ContainsKey(compassPoint))
						_stretchIndicatorTokens.Remove(compassPoint);
				}
			}
		}
Example #15
0
            public void move_rover_for_12N(CoordinatesPoint coordinatesPoint,
                                           CompassPoints direction,
                                           IEnumerable <StringMovement> commands, int cX, int cY, CompassPoints cDirection)
            {
                var roverMove = new Rover(coordinatesPoint, cDirection);

                _plate.SetSize(5, 5);
                _plate.AddRover(roverMove);
                roverMove.Move(commands, _plate);

                var lastRover = _plate.GetLastRover();

                Assert.IsNotNull(lastRover);

                var currentPosition  = lastRover.GetCoordinatesPoint();
                var currentDirection = lastRover.GetCompassPoints();

                Assert.IsNotNull(currentPosition);
                Assert.IsNotNull(currentDirection);
                Assert.AreEqual(cX, currentPosition.X);
                Assert.AreEqual(cY, currentPosition.Y);
                Assert.AreEqual(cDirection, currentDirection);
            }
        public void Nudge(CompassPoints direction)
        {
            var destination = Centre;

            switch (direction)
            {
            case CompassPoints.North:
                destination = new Point(Centre.X, Centre.Y - _bondLength / 16);
                break;

            case CompassPoints.East:
                destination = new Point(Centre.X + _bondLength / 16, Centre.Y);
                break;

            case CompassPoints.South:
                destination = new Point(Centre.X, Centre.Y + _bondLength / 16);
                break;

            case CompassPoints.West:
                destination = new Point(Centre.X - _bondLength / 16, Centre.Y);
                break;
            }
            AdjustPosition(destination - Centre);
        }
Example #17
0
        public void MoveLeft()
        {
            switch (_compassPoints)
            {
            case CompassPoints.N:
                _compassPoints = CompassPoints.W;
                break;

            case CompassPoints.E:
                _compassPoints = CompassPoints.N;
                break;

            case CompassPoints.S:
                _compassPoints = CompassPoints.E;
                break;

            case CompassPoints.W:
                _compassPoints = CompassPoints.S;
                break;

            default:
                break;
            }
        }
Example #18
0
        /// <summary>
        /// Computes the distance from a point to a compass point on the given rectangle.
        /// </summary>
        /// <param name="point">a point whose distance from a compass point on the rectangle is to be determined.</param>
        /// <param name="compassRectangle">the rectangle from which to determine the compass point position.</param>
        /// <param name="compassPoint">the point on the compass to find the distance to.</param>
        /// <returns></returns>
        private float DistanceToCompassPoint(PointF point, RectangleF compassRectangle, CompassPoints compassPoint)
        {
            PointF compassPointPosition = GetCompassPointPosition(compassPoint, compassRectangle);

            return((float)Vector.Distance(point, compassPointPosition));
        }
        public void CreateElementCharacters(Atom atom, Options options)
        {
            string module = $"{_product}.{_class}.{MethodBase.GetCurrentMethod().Name}()";

            //Point atomCentre = new Point((double)atom.X2, (double)atom.Y2);
            string atomLabel = atom.Element.Symbol;
            Rect   labelBounds;

            // Get Charge and Isotope values for use later on
            int iCharge    = atom.FormalCharge ?? 0;
            int iAbsCharge = Math.Abs(iCharge);
            int isoValue   = atom.IsotopeNumber ?? 0;

            // Get Implicit Hydrogen Count for use later on
            int implicitHCount = atom.ImplicitHydrogenCount;

            Point cursorPosition        = atom.Position;
            Point chargeCursorPosition  = atom.Position;
            Point isotopeCursorPosition = atom.Position;

            double lastOffset = 0;

            int bondCount = atom.Bonds.ToList().Count;

            #region Decide if atom label is to be displayed

            bool showLabel = true;
            if (atomLabel == "C")
            {
                if (atom.ShowSymbol.HasValue)
                {
                    showLabel = atom.ShowSymbol.Value;
                }
                else
                {
                    if (atom.IsInRing || bondCount > 1)
                    {
                        showLabel = false;
                    }

                    if (bondCount == 2)
                    {
                        Point p1 = atom.Bonds.ToList()[0].OtherAtom(atom).Position;
                        Point p2 = atom.Bonds.ToList()[1].OtherAtom(atom).Position;

                        double angle1 = Vector.AngleBetween(-(atom.Position - p1), atom.Position - p2);

                        if (Math.Abs(angle1) < 8)
                        {
                            showLabel = true;
                        }
                    }
                }

                // Force on if atom has charge
                if (iAbsCharge > 0)
                {
                    showLabel = true;
                }
                // Force on if atom has isotope value
                if (isoValue > 0)
                {
                    showLabel = true;
                }
            }

            #endregion Decide if atom label is to be displayed

            if (showLabel)
            {
                #region Set Up Atom Colours

                string atomColour = "000000";
                if (options.ColouredAtoms)
                {
                    if (atom.Element.Colour != null)
                    {
                        atomColour = atom.Element.Colour;
                        // Strip out # as OoXml does not use it
                        atomColour = atomColour.Replace("#", "");
                    }
                }

                #endregion Set Up Atom Colours

                #region Step 1 - Measure Bounding Box for all Characters of label

                double xMin = double.MaxValue;
                double yMin = double.MaxValue;
                double xMax = double.MinValue;
                double yMax = double.MinValue;

                Point thisCharacterPosition;
                for (int idx = 0; idx < atomLabel.Length; idx++)
                {
                    char         chr = atomLabel[idx];
                    TtfCharacter c   = _TtfCharacterSet[chr];
                    if (c != null)
                    {
                        thisCharacterPosition = GetCharacterPosition(cursorPosition, c);

                        xMin = Math.Min(xMin, thisCharacterPosition.X);
                        yMin = Math.Min(yMin, thisCharacterPosition.Y);
                        xMax = Math.Max(xMax, thisCharacterPosition.X + OoXmlHelper.ScaleCsTtfToCml(c.Width, _meanBondLength));
                        yMax = Math.Max(yMax, thisCharacterPosition.Y + OoXmlHelper.ScaleCsTtfToCml(c.Height, _meanBondLength));

                        if (idx < atomLabel.Length - 1)
                        {
                            // Move to next Character position
                            cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(c.IncrementX, _meanBondLength), 0);
                        }
                    }
                }

                #endregion Step 1 - Measure Bounding Box for all Characters of label

                #region Step 2 - Reset Cursor such that the text is centered about the atom's co-ordinates

                double width  = xMax - xMin;
                double height = yMax - yMin;
                cursorPosition        = new Point(atom.Position.X - width / 2, atom.Position.Y + height / 2);
                chargeCursorPosition  = new Point(cursorPosition.X, cursorPosition.Y);
                isotopeCursorPosition = new Point(cursorPosition.X, cursorPosition.Y);
                labelBounds           = new Rect(cursorPosition, new Size(width, height));

                #endregion Step 2 - Reset Cursor such that the text is centered about the atom's co-ordinates

                #region Step 3 - Place the characters

                foreach (char chr in atomLabel)
                {
                    TtfCharacter c = _TtfCharacterSet[chr];
                    if (c != null)
                    {
                        thisCharacterPosition = GetCharacterPosition(cursorPosition, c);
                        AtomLabelCharacter alc = new AtomLabelCharacter(thisCharacterPosition, c, atomColour, chr, atom.Path, atom.Parent.Path);
                        _AtomLabelCharacters.Add(alc);

                        // Move to next Character position
                        lastOffset = OoXmlHelper.ScaleCsTtfToCml(c.IncrementX, _meanBondLength);
                        cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(c.IncrementX, _meanBondLength), 0);
                        chargeCursorPosition = new Point(cursorPosition.X, cursorPosition.Y);
                    }
                }

                #endregion Step 3 - Place the characters

                #region Determine NESW

                double        baFromNorth = Vector.AngleBetween(BasicGeometry.ScreenNorth, atom.BalancingVector(true));
                CompassPoints nesw        = CompassPoints.East;

                if (bondCount == 1)
                {
                    nesw = BasicGeometry.SnapTo2EW(baFromNorth);
                }
                else
                {
                    nesw = BasicGeometry.SnapTo4NESW(baFromNorth);
                }

                #endregion Determine NESW

                #region Step 4 - Add Charge if required

                if (iCharge != 0)
                {
                    TtfCharacter hydrogenCharacter = _TtfCharacterSet['H'];

                    char         sign = '.';
                    TtfCharacter chargeSignCharacter = null;
                    if (iCharge >= 1)
                    {
                        sign = '+';
                        chargeSignCharacter = _TtfCharacterSet['+'];
                    }
                    else if (iCharge <= 1)
                    {
                        sign = '-';
                        chargeSignCharacter = _TtfCharacterSet['-'];
                    }

                    if (iAbsCharge > 1)
                    {
                        string digits = iAbsCharge.ToString();
                        // Insert digits
                        foreach (char chr in digits)
                        {
                            TtfCharacter chargeValueCharacter = _TtfCharacterSet[chr];
                            thisCharacterPosition = GetCharacterPosition(chargeCursorPosition, chargeValueCharacter);

                            // Raise the superscript Character
                            thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(chargeValueCharacter.Height * OoXmlHelper.CS_SUPERSCRIPT_RAISE_FACTOR, _meanBondLength));

                            AtomLabelCharacter alcc = new AtomLabelCharacter(thisCharacterPosition, chargeValueCharacter, atomColour, chr, atom.Path, atom.Parent.Path);
                            alcc.IsSmaller   = true;
                            alcc.IsSubScript = true;
                            _AtomLabelCharacters.Add(alcc);

                            // Move to next Character position
                            chargeCursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(chargeValueCharacter.IncrementX, _meanBondLength) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR, 0);
                            cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(chargeValueCharacter.IncrementX, _meanBondLength) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR, 0);
                        }
                    }

                    // Insert sign at raised position
                    thisCharacterPosition = GetCharacterPosition(chargeCursorPosition, chargeSignCharacter);
                    thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Height * OoXmlHelper.CS_SUPERSCRIPT_RAISE_FACTOR, _meanBondLength));
                    thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(chargeSignCharacter.Height / 2 * OoXmlHelper.CS_SUPERSCRIPT_RAISE_FACTOR, _meanBondLength));

                    AtomLabelCharacter alcs = new AtomLabelCharacter(thisCharacterPosition, chargeSignCharacter, atomColour, sign, atom.Path, atom.Parent.Path);
                    alcs.IsSmaller   = true;
                    alcs.IsSubScript = true;
                    _AtomLabelCharacters.Add(alcs);

                    if (iAbsCharge != 0)
                    {
                        cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(chargeSignCharacter.IncrementX, _meanBondLength) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR, 0);
                    }
                }

                #endregion Step 4 - Add Charge if required

                #region Step 5 - Add Implicit H if required

                if (options.ShowHydrogens && implicitHCount > 0)
                {
                    TtfCharacter hydrogenCharacter      = _TtfCharacterSet['H'];
                    string       numbers                = "012345";
                    TtfCharacter implicitValueCharacter = _TtfCharacterSet[numbers[implicitHCount]];

                    #region Add H

                    if (hydrogenCharacter != null)
                    {
                        switch (nesw)
                        {
                        case CompassPoints.North:
                            if (atom.Bonds.ToList().Count > 1)
                            {
                                cursorPosition.X = labelBounds.X
                                                   + (labelBounds.Width / 2)
                                                   - (OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Width, _meanBondLength) / 2);
                                cursorPosition.Y = cursorPosition.Y
                                                   + OoXmlHelper.ScaleCsTtfToCml(-hydrogenCharacter.Height, _meanBondLength)
                                                   - OoXmlHelper.CHARACTER_VERTICAL_SPACING;
                                if (iCharge > 0)
                                {
                                    if (implicitHCount > 1)
                                    {
                                        cursorPosition.Offset(0,
                                                              OoXmlHelper.ScaleCsTtfToCml(
                                                                  -implicitValueCharacter.Height *
                                                                  OoXmlHelper.SUBSCRIPT_SCALE_FACTOR / 2, _meanBondLength)
                                                              - OoXmlHelper.CHARACTER_VERTICAL_SPACING);
                                    }
                                }
                            }
                            break;

                        case CompassPoints.East:
                            // Leave as is
                            break;

                        case CompassPoints.South:
                            if (atom.Bonds.ToList().Count > 1)
                            {
                                cursorPosition.X = labelBounds.X + (labelBounds.Width / 2)
                                                   - (OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Width, _meanBondLength) / 2);
                                cursorPosition.Y = cursorPosition.Y
                                                   + OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Height, _meanBondLength)
                                                   + OoXmlHelper.CHARACTER_VERTICAL_SPACING;
                            }
                            break;

                        case CompassPoints.West:
                            if (implicitHCount == 1)
                            {
                                if (iAbsCharge == 0)
                                {
                                    cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(-(hydrogenCharacter.IncrementX * 2), _meanBondLength), 0);
                                }
                                else
                                {
                                    cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(-((hydrogenCharacter.IncrementX * 2 + implicitValueCharacter.IncrementX * 1.25)), _meanBondLength), 0);
                                }
                            }
                            else
                            {
                                if (iAbsCharge == 0)
                                {
                                    cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(-(hydrogenCharacter.IncrementX * 2.5), _meanBondLength), 0);
                                }
                                else
                                {
                                    cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(-((hydrogenCharacter.IncrementX * 2 + implicitValueCharacter.IncrementX * 1.25)), _meanBondLength), 0);
                                }
                            }
                            break;
                        }

                        thisCharacterPosition = GetCharacterPosition(cursorPosition, hydrogenCharacter);
                        AtomLabelCharacter alc = new AtomLabelCharacter(thisCharacterPosition, hydrogenCharacter, atomColour, 'H', atom.Path, atom.Parent.Path);
                        _AtomLabelCharacters.Add(alc);

                        // Move to next Character position
                        cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.IncrementX, _meanBondLength), 0);

                        if (nesw == CompassPoints.East)
                        {
                            chargeCursorPosition = new Point(cursorPosition.X, cursorPosition.Y);
                        }
                        if (nesw == CompassPoints.West)
                        {
                            isotopeCursorPosition = new Point(thisCharacterPosition.X, isotopeCursorPosition.Y);
                        }
                    }

                    #endregion Add H

                    #region Add number

                    if (implicitHCount > 1)
                    {
                        if (implicitValueCharacter != null)
                        {
                            thisCharacterPosition = GetCharacterPosition(cursorPosition, implicitValueCharacter);

                            // Drop the subscript Character
                            thisCharacterPosition.Offset(0, OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Width * OoXmlHelper.SUBSCRIPT_DROP_FACTOR, _meanBondLength));

                            AtomLabelCharacter alc = new AtomLabelCharacter(thisCharacterPosition, implicitValueCharacter, atomColour, numbers[implicitHCount], atom.Path, atom.Parent.Path);
                            alc.IsSmaller   = true;
                            alc.IsSubScript = true;
                            _AtomLabelCharacters.Add(alc);

                            // Move to next Character position
                            cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(implicitValueCharacter.IncrementX, _meanBondLength) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR, 0);
                        }
                    }

                    #endregion Add number
                }

                #endregion Step 5 - Add Implicit H if required

                #region Step 6 - Add IsoTope Number if required

                if (isoValue > 0)
                {
                    string digits = isoValue.ToString();

                    xMin = double.MaxValue;
                    yMin = double.MaxValue;
                    xMax = double.MinValue;
                    yMax = double.MinValue;

                    Point isoOrigin = isotopeCursorPosition;

                    // Calculate width of digits
                    foreach (char chr in digits)
                    {
                        TtfCharacter c = _TtfCharacterSet[chr];
                        thisCharacterPosition = GetCharacterPosition(isotopeCursorPosition, c);

                        // Raise the superscript Character
                        thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(c.Height * OoXmlHelper.CS_SUPERSCRIPT_RAISE_FACTOR, _meanBondLength));

                        xMin = Math.Min(xMin, thisCharacterPosition.X);
                        yMin = Math.Min(yMin, thisCharacterPosition.Y);
                        xMax = Math.Max(xMax, thisCharacterPosition.X + OoXmlHelper.ScaleCsTtfToCml(c.Width, _meanBondLength));
                        yMax = Math.Max(yMax, thisCharacterPosition.Y + OoXmlHelper.ScaleCsTtfToCml(c.Height, _meanBondLength));

                        // Move to next Character position
                        isotopeCursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(c.IncrementX, _meanBondLength) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR, 0);
                    }

                    // Re-position Isotope Cursor
                    width = xMax - xMin;
                    isotopeCursorPosition = new Point(isoOrigin.X - width, isoOrigin.Y);

                    // Insert digits
                    foreach (char chr in digits)
                    {
                        TtfCharacter c = _TtfCharacterSet[chr];
                        thisCharacterPosition = GetCharacterPosition(isotopeCursorPosition, c);

                        // Raise the superscript Character
                        thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(c.Height * OoXmlHelper.CS_SUPERSCRIPT_RAISE_FACTOR, _meanBondLength));

                        AtomLabelCharacter alcc = new AtomLabelCharacter(thisCharacterPosition, c, atomColour, chr, atom.Path, atom.Parent.Path);
                        alcc.IsSmaller   = true;
                        alcc.IsSubScript = true;
                        _AtomLabelCharacters.Add(alcc);

                        // Move to next Character position
                        isotopeCursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(c.IncrementX, _meanBondLength) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR, 0);
                    }
                }

                #endregion Step 6 - Add IsoTope Number if required

                #region Step 7 - Create Convex Hull

                _convexhHulls.Add(atom.Path, ConvexHull(atom.Path));

                #endregion Step 7 - Create Convex Hull
            }
        }
        public void CreateFunctionalGroupCharacters(Atom atom, Options options)
        {
            FunctionalGroup fg          = atom.Element as FunctionalGroup;
            double          baFromNorth = Vector.AngleBetween(BasicGeometry.ScreenNorth, atom.BalancingVector(true));

            CompassPoints nesw    = BasicGeometry.SnapTo2EW(baFromNorth);
            bool          reverse = nesw == CompassPoints.West;

            #region Set Up Atom Colours

            string atomColour = "000000";
            if (options.ColouredAtoms)
            {
                if (!string.IsNullOrEmpty(fg.Colour))
                {
                    atomColour = fg.Colour;
                    // Strip out # as OoXml does not use it
                    atomColour = atomColour.Replace("#", "");
                }
            }

            #endregion Set Up Atom Colours

            List <FunctionalGroupTerm> terms = fg.ExpandIntoTerms(reverse);

            #region Step 1 - Generate the characters and measure Bounding Boxes

            var cursorPosition = atom.Position;

            List <AtomLabelCharacter> fgCharacters = new List <AtomLabelCharacter>();
            TtfCharacter hydrogenCharacter         = _TtfCharacterSet['H'];

            Rect fgBoundingBox     = Rect.Empty;
            Rect anchorBoundingBox = Rect.Empty;

            foreach (var term in terms)
            {
                foreach (var part in term.Parts)
                {
                    foreach (char c in part.Text)
                    {
                        Rect bb = AddCharacter(c, part.Type);
                        fgBoundingBox.Union(bb);
                        if (term.IsAnchor)
                        {
                            anchorBoundingBox.Union(bb);
                        }
                    }
                }
            }

            #endregion Step 1 - Generate the characters and measure Bounding Boxes

            #region Step 2 - Move all characters such that the anchor term is centered on the atom position

            double offsetX;
            double offsetY;
            if (reverse)
            {
                offsetX = fgBoundingBox.Width - anchorBoundingBox.Width / 2;
                offsetY = anchorBoundingBox.Height / 2;
            }
            else
            {
                offsetX = anchorBoundingBox.Width / 2;
                offsetY = anchorBoundingBox.Height / 2;
            }

            offsetY = offsetY + anchorBoundingBox.Top - atom.Position.Y;

            foreach (var alc in fgCharacters)
            {
                alc.Position = new Point(alc.Position.X - offsetX, alc.Position.Y - offsetY);
            }

            #endregion Step 2 - Move all characters such that the anchor term is centered on the atom position

            #region Step 3 - Transfer characters into main list

            foreach (var alc in fgCharacters)
            {
                _AtomLabelCharacters.Add(alc);
            }

            #endregion Step 3 - Transfer characters into main list

            #region Step 4 - Convex Hull

            _convexhHulls.Add(atom.Path, ConvexHull(atom.Path));

            #endregion Step 4 - Convex Hull

            // Local Function
            Rect AddCharacter(char c, FunctionalGroupPartType type)
            {
                TtfCharacter ttf = _TtfCharacterSet[c];
                var          thisCharacterPosition = GetCharacterPosition(cursorPosition, ttf);
                var          alc = new AtomLabelCharacter(thisCharacterPosition, ttf, atomColour, c, atom.Path, atom.Parent.Path);

                alc.IsSubScript   = type == FunctionalGroupPartType.Subscript;
                alc.IsSuperScript = type == FunctionalGroupPartType.Superscript;
                alc.IsSmaller     = alc.IsSubScript || alc.IsSuperScript;

                Rect thisBoundingBox;

                if (alc.IsSmaller)
                {
                    // Start by assuming it's SubScript
                    thisCharacterPosition.Offset(0, OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Height * OoXmlHelper.SUBSCRIPT_DROP_FACTOR, _meanBondLength));
                    if (alc.IsSuperScript)
                    {
                        // Shift up by height of H to make it SuperScript
                        thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Height, _meanBondLength));
                    }

                    // Reset the character's position
                    alc.Position = thisCharacterPosition;

                    thisBoundingBox = new Rect(alc.Position,
                                               new Size(OoXmlHelper.ScaleCsTtfToCml(alc.Character.Width, _meanBondLength) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR,
                                                        OoXmlHelper.ScaleCsTtfToCml(alc.Character.Height, _meanBondLength) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR));

                    cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(alc.Character.IncrementX, _meanBondLength) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR, 0);
                }
                else
                {
                    thisBoundingBox = new Rect(alc.Position,
                                               new Size(OoXmlHelper.ScaleCsTtfToCml(alc.Character.Width, _meanBondLength),
                                                        OoXmlHelper.ScaleCsTtfToCml(alc.Character.Height, _meanBondLength)));

                    cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(alc.Character.IncrementX, _meanBondLength), 0);
                }

                fgCharacters.Add(alc);

                return(thisBoundingBox);
            }
        }
		/// <summary>
		/// Computes the position on a given rectangle that corresponds to the given compass point.
		/// </summary>
		/// <param name="compassPoint">the compass point whose position on the rectangle is to be determined.</param>
		/// <param name="rectangle">the rectangle.</param>
		/// <returns>the point on the rectangle that corresponds to the given compass point.</returns>
		private PointF GetCompassPointPosition(CompassPoints compassPoint, RectangleF rectangle)
		{
			float top = rectangle.Top;
			float left = rectangle.Left;
			float right = rectangle.Right;
			float bottom = rectangle.Bottom;
			
			float centreX = left;
			if (left != right)
				centreX = rectangle.Left + rectangle.Width / 2F;

			float centreY = top;
			if (top != bottom)
				centreY = rectangle.Top + rectangle.Height / 2F;

			switch (compassPoint)
			{
				case CompassPoints.NorthWest:
					return new PointF(left, top);
				case CompassPoints.NorthEast:
					return new PointF(right, top);
				case CompassPoints.SouthEast:
					return new PointF(right, bottom);
				case CompassPoints.SouthWest:
					return new PointF(left, bottom);
				case CompassPoints.North:
					return new PointF(centreX, top);
				case CompassPoints.East:
					return new PointF(right, centreY);
				case CompassPoints.South:
					return new PointF(centreX, bottom);
				default: //CompassPoints.West:
					return new PointF(left, centreY);
			}
		}
Example #22
0
 public Rover(CoordinatesPoint coordinatesPoint, CompassPoints compassPoints)
 {
     _compassPoints    = compassPoints;
     _coordinatesPoint = coordinatesPoint;
 }
Example #23
0
 public static double ToDegrees(this CompassPoints cp)
 {
     return(45 * (int)cp);
 }
Example #24
0
        /// <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);
        }
Example #25
0
        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 SetRoverCurrentPosition(CoordinatesPoint point, CompassPoints cp)
        {
            var rover = new Rover(point, cp);

            _plate.AddRover(rover);
        }
        public void CreateCharacters(Atom atom, Options options)
        {
            string module = $"{_product}.{_class}.{MethodBase.GetCurrentMethod().Name}()";
            //Debug.WriteLine("Atom: " + atom.Id + " is " + atom.ElementType);

            //Point atomCentre = new Point((double)atom.X2, (double)atom.Y2);
            string atomLabel = atom.Element.Symbol;
            Rect   labelBounds;

            //Debug Code
            //atomLabel = "H" + "abcdefHghijklmnopqrstuvwxyz" + "H";
            //atomLabel = "ABCHDEFGHIJKLMNOHPQRSHTUVWXYZ";
            //atomLabel = "-+H01234567890H+-";

            // Get Charge and Isotope valuesfor use later on
            int iCharge    = atom.FormalCharge ?? 0;
            int iAbsCharge = Math.Abs(iCharge);
            int isoValue   = atom.IsotopeNumber ?? 0;

            // Get Implicit Hydrogen Count for use later on
            int implicitHCount = atom.ImplicitHydrogenCount;

            Point cursorPosition        = atom.Position;
            Point chargeCursorPosition  = atom.Position;
            Point isotopeCursorPosition = atom.Position;

            double lastOffset = 0;

            //Debug.WriteLine("  X: " + atom.X2 + " Y: " + atom.Y2 + " Implicit H Count: " + implicitHCount);

            int ringCount = atom.Rings.Count();
            int bondCount = atom.Bonds.Count;

            //var bv = atom.BalancingVector;
            //_telemetry.Write(module, "Debugging", $"Atom {atomLabel} [{atom.Id}] at {atom.Position} BalancingVector {bv} [{CoordinateTool.BearingOfVector(bv)}°]");

            #region Decide if atom label is to be displayed

            bool showLabel = true;
            if (atomLabel == "C")
            {
                if (!options.ShowCarbons)
                {
                    if (ringCount > 0 || bondCount > 1)
                    {
                        showLabel = false;
                    }

                    if (bondCount == 2)
                    {
                        Point p1 = atom.Bonds[0].OtherAtom(atom).Position;
                        Point p2 = atom.Bonds[1].OtherAtom(atom).Position;

                        double angle1 = Vector.AngleBetween(-(atom.Position - p1), atom.Position - p2);

                        if (Math.Abs(angle1) < 8)
                        {
                            showLabel = true;
                        }
                    }

                    // Force on if atom has charge
                    if (iAbsCharge > 0)
                    {
                        showLabel = true;
                    }
                    // Force on if atom has isotope value
                    if (isoValue > 0)
                    {
                        showLabel = true;
                    }
                }
            }

            #endregion Decide if atom label is to be displayed

            if (showLabel)
            {
                #region Set Up Atom Colours

                string atomColour = "000000";
                if (options.ColouredAtoms)
                {
                    if ((atom.Element as Element).Colour != null)
                    {
                        atomColour = ((Element)atom.Element).Colour;
                        // Strip out # as OoXml does not use it
                        atomColour = atomColour.Replace("#", "");
                    }
                }

                #endregion Set Up Atom Colours

                #region Step 1 - Measure Bounding Box for all Characters of label

                double xMin = double.MaxValue;
                double yMin = double.MaxValue;
                double xMax = double.MinValue;
                double yMax = double.MinValue;

                Point thisCharacterPosition;
                for (int idx = 0; idx < atomLabel.Length; idx++)
                {
                    char         chr = atomLabel[idx];
                    TtfCharacter c   = m_TtfCharacterSet[chr];
                    if (c != null)
                    {
                        thisCharacterPosition = GetCharacterPosition(cursorPosition, c);

                        xMin = Math.Min(xMin, thisCharacterPosition.X);
                        yMin = Math.Min(yMin, thisCharacterPosition.Y);
                        xMax = Math.Max(xMax, thisCharacterPosition.X + OoXmlHelper.ScaleCsTtfToCml(c.Width));
                        yMax = Math.Max(yMax, thisCharacterPosition.Y + OoXmlHelper.ScaleCsTtfToCml(c.Height));

                        // Uncomment the following to disable offsetting for terminal atoms
                        //if (bonds.Count == 1)
                        //{
                        //    break;
                        //}

                        if (idx < atomLabel.Length - 1)
                        {
                            // Move to next Character position
                            cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(c.IncrementX), 0);
                        }
                    }
                }

                #endregion Step 1 - Measure Bounding Box for all Characters of label

                #region Step 2 - Reset Cursor such that the text is centered about the atom's co-ordinates

                double width  = xMax - xMin;
                double height = yMax - yMin;
                cursorPosition        = new Point(atom.Position.X - width / 2, atom.Position.Y + height / 2);
                chargeCursorPosition  = new Point(cursorPosition.X, cursorPosition.Y);
                isotopeCursorPosition = new Point(cursorPosition.X, cursorPosition.Y);
                labelBounds           = new Rect(cursorPosition, new Size(width, height));
                //_telemetry.Write(module, "Debugging", $"Atom {atomLabel} [{atom.Id}] Label Bounds {labelBounds}");

                #endregion Step 2 - Reset Cursor such that the text is centered about the atom's co-ordinates

                #region Step 3 - Place the characters

                foreach (char chr in atomLabel)
                {
                    TtfCharacter c = m_TtfCharacterSet[chr];
                    if (c != null)
                    {
                        thisCharacterPosition = GetCharacterPosition(cursorPosition, c);
                        AtomLabelCharacter alc = new AtomLabelCharacter(thisCharacterPosition, c, atomColour, chr, atom.Id);
                        m_AtomLabelCharacters.Add(alc);

                        // Move to next Character position
                        lastOffset = OoXmlHelper.ScaleCsTtfToCml(c.IncrementX);
                        cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(c.IncrementX), 0);
                        chargeCursorPosition = new Point(cursorPosition.X, cursorPosition.Y);
                    }
                }

                #endregion Step 3 - Place the characters

                #region Determine NESW

                double        baFromNorth = Vector.AngleBetween(BasicGeometry.ScreenNorth(), atom.BalancingVector);
                CompassPoints nesw        = CompassPoints.East;

                if (bondCount == 1)
                {
                    nesw = BasicGeometry.SnapTo2EW(baFromNorth);
                }
                else
                {
                    nesw = BasicGeometry.SnapTo4NESW(baFromNorth);
                }

                #endregion Determine NESW

                #region Step 4 - Add Implicit H if required

                if (options.ShowHydrogens && implicitHCount > 0)
                {
                    TtfCharacter hydrogenCharacter      = m_TtfCharacterSet['H'];
                    string       numbers                = "012345";
                    TtfCharacter implicitValueCharacter = m_TtfCharacterSet[numbers[implicitHCount]];

                    #region Add H

                    if (hydrogenCharacter != null)
                    {
                        switch (nesw)
                        {
                        case CompassPoints.North:
                            if (atom.Bonds.Count > 1)
                            {
                                cursorPosition.X = labelBounds.X + (labelBounds.Width / 2) - (OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Width) / 2);
                                cursorPosition.Y = cursorPosition.Y +
                                                   OoXmlHelper.ScaleCsTtfToCml(-hydrogenCharacter.Height) -
                                                   OoXmlHelper.CHARACTER_CLIPPING_MARGIN;
                                if (iCharge > 0)
                                {
                                    if (implicitHCount > 1)
                                    {
                                        cursorPosition.Offset(0, OoXmlHelper.ScaleCsTtfToCml(-implicitValueCharacter.Height * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR / 2) - OoXmlHelper.CHARACTER_CLIPPING_MARGIN);
                                    }
                                }
                            }
                            break;

                        case CompassPoints.East:
                            // Leave as is
                            break;

                        case CompassPoints.South:
                            if (atom.Bonds.Count > 1)
                            {
                                cursorPosition.X = labelBounds.X + (labelBounds.Width / 2) - (OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Width) / 2);
                                cursorPosition.Y = cursorPosition.Y +
                                                   OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Height) +
                                                   OoXmlHelper.CHARACTER_CLIPPING_MARGIN;
                            }
                            break;

                        case CompassPoints.West:
                            if (implicitHCount == 1)
                            {
                                cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(-(hydrogenCharacter.IncrementX * 2)), 0);
                            }
                            else
                            {
                                cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(-(hydrogenCharacter.IncrementX * 2.5)), 0);
                            }
                            break;
                        }

                        //_telemetry.Write(module, "Debugging", $"Adding H at {cursorPosition}");
                        thisCharacterPosition = GetCharacterPosition(cursorPosition, hydrogenCharacter);
                        AtomLabelCharacter alc = new AtomLabelCharacter(thisCharacterPosition, hydrogenCharacter, atomColour, 'H', atom.Id);
                        m_AtomLabelCharacters.Add(alc);

                        // Move to next Character position
                        cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.IncrementX), 0);

                        if (nesw == CompassPoints.East)
                        {
                            chargeCursorPosition = new Point(cursorPosition.X, cursorPosition.Y);
                        }
                        if (nesw == CompassPoints.West)
                        {
                            isotopeCursorPosition = new Point(thisCharacterPosition.X, isotopeCursorPosition.Y);
                        }
                    }

                    #endregion Add H

                    #region Add number

                    if (implicitHCount > 1)
                    {
                        if (implicitValueCharacter != null)
                        {
                            thisCharacterPosition = GetCharacterPosition(cursorPosition, implicitValueCharacter);

                            // Drop the subscript Character
                            thisCharacterPosition.Offset(0, OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Width * OoXmlHelper.SUBSCRIPT_DROP_FACTOR));

                            AtomLabelCharacter alc = new AtomLabelCharacter(thisCharacterPosition, implicitValueCharacter, atomColour, numbers[implicitHCount], atom.Id);
                            alc.IsSubScript = true;
                            m_AtomLabelCharacters.Add(alc);

                            // Move to next Character position
                            cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(implicitValueCharacter.IncrementX) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR, 0);
                        }
                    }

                    #endregion Add number
                }

                #endregion Step 4 - Add Implicit H if required

                #region Step 5 - Add Charge if required

                if (iCharge != 0)
                {
                    TtfCharacter hydrogenCharacter = m_TtfCharacterSet['H'];

                    char         sign = '.';
                    TtfCharacter chargeSignCharacter = null;
                    if (iCharge >= 1)
                    {
                        sign = '+';
                        chargeSignCharacter = m_TtfCharacterSet['+'];
                    }
                    else if (iCharge <= 1)
                    {
                        sign = '-';
                        chargeSignCharacter = m_TtfCharacterSet['-'];
                    }

                    if (iAbsCharge > 1)
                    {
                        string digits = iAbsCharge.ToString();
                        // Insert digits
                        foreach (char chr in digits)
                        {
                            TtfCharacter chargeValueCharacter = m_TtfCharacterSet[chr];
                            thisCharacterPosition = GetCharacterPosition(chargeCursorPosition, chargeValueCharacter);

                            // Raise the superscript Character
                            thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(chargeValueCharacter.Height * OoXmlHelper.CS_SUPERSCRIPT_RAISE_FACTOR));

                            AtomLabelCharacter alcc = new AtomLabelCharacter(thisCharacterPosition, chargeValueCharacter, atomColour, chr, atom.Id);
                            alcc.IsSubScript = true;
                            m_AtomLabelCharacters.Add(alcc);

                            // Move to next Character position
                            chargeCursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(chargeValueCharacter.IncrementX) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR, 0);
                        }
                    }

                    // Insert sign at raised position
                    thisCharacterPosition = GetCharacterPosition(chargeCursorPosition, chargeSignCharacter);
                    thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Height * OoXmlHelper.CS_SUPERSCRIPT_RAISE_FACTOR));
                    thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(chargeSignCharacter.Height / 2 * OoXmlHelper.CS_SUPERSCRIPT_RAISE_FACTOR));

                    AtomLabelCharacter alcs = new AtomLabelCharacter(thisCharacterPosition, chargeSignCharacter, atomColour, sign, atom.Id);
                    alcs.IsSubScript = true;
                    m_AtomLabelCharacters.Add(alcs);
                }

                #endregion Step 5 - Add Charge if required

                #region Step 6 Add IsoTope Number if required

                if (isoValue > 0)
                {
                    string digits = isoValue.ToString();

                    xMin = double.MaxValue;
                    yMin = double.MaxValue;
                    xMax = double.MinValue;
                    yMax = double.MinValue;

                    Point isoOrigin = isotopeCursorPosition;

                    // Calculate width of digits
                    foreach (char chr in digits)
                    {
                        TtfCharacter c = m_TtfCharacterSet[chr];
                        thisCharacterPosition = GetCharacterPosition(isotopeCursorPosition, c);

                        // Raise the superscript Character
                        thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(c.Height * OoXmlHelper.CS_SUPERSCRIPT_RAISE_FACTOR));

                        xMin = Math.Min(xMin, thisCharacterPosition.X);
                        yMin = Math.Min(yMin, thisCharacterPosition.Y);
                        xMax = Math.Max(xMax, thisCharacterPosition.X + OoXmlHelper.ScaleCsTtfToCml(c.Width));
                        yMax = Math.Max(yMax, thisCharacterPosition.Y + OoXmlHelper.ScaleCsTtfToCml(c.Height));

                        // Move to next Character position
                        isotopeCursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(c.IncrementX) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR, 0);
                    }

                    // Re-position Isotope Cursor
                    width = xMax - xMin;
                    isotopeCursorPosition = new Point(isoOrigin.X - width, isoOrigin.Y);

                    // Insert digits
                    foreach (char chr in digits)
                    {
                        TtfCharacter c = m_TtfCharacterSet[chr];
                        thisCharacterPosition = GetCharacterPosition(isotopeCursorPosition, c);

                        // Raise the superscript Character
                        thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(c.Height * OoXmlHelper.CS_SUPERSCRIPT_RAISE_FACTOR));

                        AtomLabelCharacter alcc = new AtomLabelCharacter(thisCharacterPosition, c, atomColour, chr, atom.Id);
                        alcc.IsSubScript = true;
                        m_AtomLabelCharacters.Add(alcc);

                        // Move to next Character position
                        isotopeCursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(c.IncrementX) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR, 0);
                    }
                }

                #endregion Step 6 Add IsoTope Number if required
            }
        }
		/// <summary>
		/// Computes the distance from a point to a compass point on the given rectangle.
		/// </summary>
		/// <param name="point">a point whose distance from a compass point on the rectangle is to be determined.</param>
		/// <param name="compassRectangle">the rectangle from which to determine the compass point position.</param>
		/// <param name="compassPoint">the point on the compass to find the distance to.</param>
		/// <returns></returns>
		private float DistanceToCompassPoint(PointF point, RectangleF compassRectangle, CompassPoints compassPoint)
		{
			PointF compassPointPosition = GetCompassPointPosition(compassPoint, compassRectangle);
			return (float)Vector.Distance(point, compassPointPosition);
		}