コード例 #1
0
ファイル: Metrics.cs プロジェクト: notator/Moritz
        public GroupMetrics(string ID_Type, Metrics metrics)
            : base()
        {
            MetricsList.Add(metrics);

            _objectType = ID_Type;
            _originX = metrics.OriginX;
            _originY = metrics.OriginY;
            _top = metrics.Top;
            _right = metrics.Right;
            _bottom = metrics.Bottom;
            _left = metrics.Left;
        }
コード例 #2
0
ファイル: ChordMetrics.cs プロジェクト: notator/Moritz
 /// <summary>
 /// If isBelow, metrics.Top is moved to bottomBoundary + bottomPadding and bottomBoundary is then set to metrics.Bottom
 /// otherwise metrics.Bottom is moved to topBoundary - topPadding ans topBoundary is then set to metrics.Top.
 /// Does nothing if metrics is null.
 /// </summary>
 private void MoveMetrics(Metrics metrics, bool isBelow,
     ref float topBoundary, float topPadding,
     ref float bottomBoundary, float bottomPadding)
 {
     Debug.Assert(NoteheadExtendersMetrics == null);
     if(isBelow)
     {
         MoveBelowBottomBoundary(metrics, ref bottomBoundary, bottomPadding);
     }
     else
     {
         MoveAboveTopBoundary(metrics, ref topBoundary, topPadding);
     }
     SetExternalBoundary();
 }
コード例 #3
0
ファイル: ChordMetrics.cs プロジェクト: notator/Moritz
 private void MoveBelowBottomBoundary(Metrics metrics, ref float bottomBoundary, float padding)
 {
     Debug.Assert(padding >= 0.0F);
     float newTop = bottomBoundary + padding;
     metrics.Move(0F, newTop - metrics.Top);
     bottomBoundary = metrics.Bottom;
     SetExternalBoundary();
 }
コード例 #4
0
ファイル: ChordMetrics.cs プロジェクト: notator/Moritz
 private void MoveAboveTopBoundary(Metrics metrics, ref float topBoundary, float padding)
 {
     Debug.Assert(padding >= 0.0F);
     float newBottom = topBoundary - padding;
     metrics.Move(0F, newBottom - metrics.Bottom);
     topBoundary = metrics.Top;
     SetExternalBoundary();
 }
コード例 #5
0
ファイル: ChordMetrics.cs プロジェクト: notator/Moritz
        /// <summary>
        /// Returns the stem which the otherChordTopDownHeadsMetrics would have if their duration class was crotchet.
        /// DummyStemMetrics are used when aligning synchronous chords.
        /// </summary>
        private StemMetrics DummyStemMetrics(
            List<HeadMetrics> otherChordTopDownHeadsMetrics,
            VerticalDir stemDirection,
            float fontHeight,
            Metrics flagsBlockMetrics,
            BeamBlock beamBlock,
            float strokeWidth)
        {
            List<HeadMetrics> tempTopDownHeadsMetrics = new List<HeadMetrics>();
            foreach(HeadMetrics headMetrics in otherChordTopDownHeadsMetrics)
            {
                HeadMetrics newHeadMetrics = new HeadMetrics(headMetrics, DurationClass.crotchet);
                tempTopDownHeadsMetrics.Add(newHeadMetrics);
            }

            return NewStemMetrics(tempTopDownHeadsMetrics, stemDirection, fontHeight, flagsBlockMetrics, beamBlock, strokeWidth);
        }
コード例 #6
0
ファイル: TimeSignature.cs プロジェクト: notator/MNXtoSVG
 /// <summary>
 /// Writes a time signature to the SVG file.
 /// The metrics have been set in SvgSystem.Justify()
 /// </summary>
 public void WriteSVG(SvgWriter w, string timeSigSignature, double originX, double originY)
 {
     Metrics.WriteSVG(w);
 }
コード例 #7
0
ファイル: ChordMetrics.cs プロジェクト: notator/Moritz
 /// <summary>
 /// Returns the (positive) amount by which to raise the metrics argument
 /// so that its bottom is level with the top of a chord component which 
 /// it overlaps.
 /// The argument metrics is padded on all sides using the padding argument.
 /// </summary>
 public new float OverlapHeight(Metrics metrics, float padding)
 {
     float maxOverlapHeight = float.MinValue;
     float overlap = 0;
     #region _stemMetrics
     if(_stemMetrics != null)
     {
         overlap = _stemMetrics.OverlapHeight(metrics, padding);
         if(overlap != 0F)
             maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap;
     }
     #endregion
     #region _flagsBlockMetrics
     if(_flagsBlockMetrics != null)
     {
         overlap = _flagsBlockMetrics.OverlapHeight(metrics, padding);
         if(overlap != 0F)
             maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap;
     }
     #endregion
     #region _topDownHeadsMetrics
     if(_headsMetricsTopDown != null)
     {
         foreach(HeadMetrics headMetric in _headsMetricsTopDown)
         {
             overlap = headMetric.OverlapHeight(metrics, padding);
             if(overlap != 0F)
                 maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap;
         }
     }
     #endregion
     #region _accidentalsMetrics
     if(_topDownAccidentalsMetrics != null)
     {
         foreach(AccidentalMetrics accidentalMetric in _topDownAccidentalsMetrics)
         {
             overlap = accidentalMetric.OverlapHeight(metrics, padding);
             if(overlap != 0F)
                 maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap;
         }
     }
     #endregion
     #region _ledgerlineBlocksMetrics
     if(_upperLedgerlineBlockMetrics != null)
     {
         overlap = _upperLedgerlineBlockMetrics.OverlapHeight(metrics, padding);
         if(overlap != 0F)
             maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap;
     }
     if(_lowerLedgerlineBlockMetrics != null)
     {
         overlap = _lowerLedgerlineBlockMetrics.OverlapHeight(metrics, padding);
         if(overlap != 0F)
             maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap;
     }
     #endregion
     #region
     if(_cautionaryBracketsMetrics != null)
     {
         foreach(CautionaryBracketMetrics cautionaryBracketMetrics in _cautionaryBracketsMetrics)
         {
             overlap = cautionaryBracketMetrics.OverlapHeight(metrics, padding);
             if(overlap != 0F)
                 maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap;
         }
     }
     #endregion
     #region _ornamentMetrics
     if(_ornamentMetrics != null)
     {
         overlap = _ornamentMetrics.OverlapHeight(metrics, padding);
         if(overlap != 0F)
             maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap;
     }
     #endregion
     #region _lyricMetrics
     if(_lyricMetrics != null)
     {
         overlap = _lyricMetrics.OverlapHeight(metrics, padding);
         if(overlap != 0F)
             maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap;
     }
     #endregion
     #region _dynamicMetrics
     if(_dynamicMetrics != null)
     {
         overlap = _dynamicMetrics.OverlapHeight(metrics, padding);
         if(overlap != 0F)
             maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap;
     }
     #endregion
     #region NoteheadExtendersMetricsBefore
     if(NoteheadExtendersMetricsBefore != null)
     {
         foreach(NoteheadExtenderMetrics nem in NoteheadExtendersMetricsBefore)
         {
             overlap = nem.OverlapHeight(metrics, padding);
             if(overlap != 0F)
                 maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap;
         }
     }
     #endregion
     return maxOverlapHeight;
 }
コード例 #8
0
ファイル: ChordMetrics.cs プロジェクト: notator/Moritz
 private void SetBoundary(Metrics metrics)
 {
     _top = _top < metrics.Top ? _top : metrics.Top;
     _right = _right > metrics.Right ? _right : metrics.Right;
     _bottom = _bottom > metrics.Bottom ? _bottom : metrics.Bottom;
     _left = _left < metrics.Left ? _left : metrics.Left;
 }
コード例 #9
0
ファイル: Metrics.cs プロジェクト: notator/Moritz
        /// <summary>
        /// Use this function to check all atomic overlaps (single character or line boxMetrics).
        /// </summary>
        /// <param name="metrics"></param>
        /// <returns></returns>
        public bool Overlaps(Metrics metrics)
        {
            bool verticalOverlap = true;
            bool horizontalOverlap = true;
            if((metrics.Top > Bottom) || (metrics.Bottom < Top))
                verticalOverlap = false;
            if((metrics.Left > Right) || (metrics.Right < Left))
                horizontalOverlap = false;

            return verticalOverlap && horizontalOverlap;
        }
コード例 #10
0
ファイル: Metrics.cs プロジェクト: notator/Moritz
        /// <summary>
        /// If the padded argument overlaps this metrics horizontally, 
        /// and this.Top is smaller than paddedArgument.Bottom,
        /// this.Bottom - paddedArgument.Right is returned. 
        /// Otherwise 0F is returned.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public float OverlapHeight(Metrics arg, float padding)
        {
            float newArgRight = arg.Right + padding;
            float newArgLeft = arg.Left - padding;
            float newArgBottom = arg.Bottom + padding;

            bool horizontalOverlap = true;
            if((newArgRight < Left) || (newArgLeft > Right))
                horizontalOverlap = false;

            if(horizontalOverlap && newArgBottom > Top)
            {
                float overlap = newArgBottom - Top;
                return (overlap);
            }
            else
                return 0F;
        }
コード例 #11
0
 /// <summary>
 /// Adds the metrics to the MetricsList and includes it in this object's boundary.
 /// The boundary is used for collision checking. All objects which should move together with this object
 /// must be added to the MetricsList.
 /// </summary>
 /// <param name="metrics"></param>
 public virtual void Add(Metrics metrics)
 {
     MetricsList.Add(metrics);
     ResetBoundary();
 }
コード例 #12
0
ファイル: Edge.cs プロジェクト: suvjunmd/Moritz
 /// <summary>
 /// If the top edge of the metrics object lies above any line in this top edge,
 /// then this top edge is adjusted accordingly.
 /// </summary>
 public override void Add(Metrics metrics)
 {
     HLine hLine = new HLine(metrics.Left, metrics.Right, metrics.Top);
     AddLineToUpperEdge(hLine);
 }
コード例 #13
0
ファイル: Edge.cs プロジェクト: suvjunmd/Moritz
 /// <summary>
 /// Adds either the top or the bottom of the metrics object to this horizontal edge,
 /// depending on whether this is a top or bottom edge.
 /// </summary>
 /// <param name="metrics"></param>
 public abstract void Add(Metrics metrics);
コード例 #14
0
ファイル: Edge.cs プロジェクト: suvjunmd/Moritz
 /// <summary>
 /// If the bottom edge of the metrics object lies below any line in this bottom edge,
 /// then this bottom edge is adjusted accordingly.
 /// </summary>
 public override void Add(Metrics metrics)
 {
     HLine line = new HLine(metrics.Left, metrics.Right, metrics.Bottom);
     Add(line);
 }
コード例 #15
0
ファイル: ChordMetrics.cs プロジェクト: notator/Moritz
 /// <summary>
 /// Sets the stem. Pass flagsBlockMetrics=null for duration classes having no flags.
 /// </summary>
 private StemMetrics NewStemMetrics(List<HeadMetrics> topDownHeadsMetrics, ChordSymbol chord, Metrics flagsBlockMetrics, float strokeWidth)
 {
     return NewStemMetrics(topDownHeadsMetrics, chord.Stem.Direction, chord.FontHeight, flagsBlockMetrics, chord.BeamBlock, strokeWidth);
 }
コード例 #16
0
ファイル: ChordMetrics.cs プロジェクト: notator/Moritz
        private StemMetrics NewStemMetrics(
            List<HeadMetrics> topDownHeadsMetrics, 
            VerticalDir stemDirection, 
            float fontHeight, 
            Metrics flagsBlockMetrics,
            BeamBlock beamBlock,
            float strokeWidth)
        {
            HeadMetrics outerNotehead = FindOuterNotehead(topDownHeadsMetrics, stemDirection);
            HeadMetrics innerNotehead = FindInnerNotehead(topDownHeadsMetrics, stemDirection);
            string noteheadID = outerNotehead.ID_Type;
            NoteheadStemPositions_px nspPX = CLichtFontMetrics.ClichtNoteheadStemPositionsDictPX[noteheadID];
            float outerNoteheadAlignmentY = (outerNotehead.Bottom + outerNotehead.Top) / 2F;
            float innerNoteheadAlignmentY = (innerNotehead.Bottom + innerNotehead.Top) / 2F;
            float delta = _gap * 0.1F;
            float octave = (_gap * 3.5F) + delta; // a little more than 1 octave
            float sixth = (_gap * 2.5F) + delta; // a little more than 1 sixth

            float top = 0F;
            float bottom = 0F;
            float x = 0F;
            if(stemDirection == VerticalDir.up)
            {
                x = outerNotehead.RightStemX - (strokeWidth / 2);
                bottom = outerNoteheadAlignmentY + (nspPX.RightStemY_px * fontHeight);
                if(beamBlock != null)
                {
                    top = beamBlock.DefaultStemTipY;
                }
                else
                {
                    if(flagsBlockMetrics != null)
                    {
                        top = flagsBlockMetrics.Top;
                    }
                    else
                        top = innerNoteheadAlignmentY - octave;

                    if(top > (_gap * 2))
                    {
                        top = (_gap * 2) - delta;
                    }
                }
            }
            else // stem is down
            {
                x = outerNotehead.LeftStemX + (strokeWidth / 2);
                top = outerNoteheadAlignmentY + (nspPX.LeftStemY_px * fontHeight);
                if(beamBlock != null)
                {
                    bottom = beamBlock.DefaultStemTipY;
                }
                else
                {
                    if(flagsBlockMetrics != null)
                    {
                        bottom = flagsBlockMetrics.Bottom;
                    }
                    else
                        bottom = innerNoteheadAlignmentY + octave;

                    if(bottom < (_gap * 2))
                    {
                        bottom = (_gap * 2) + delta;
                    }
                }
            }

            StemMetrics stemMetrics = new StemMetrics(top, x, bottom, strokeWidth, stemDirection);
            return stemMetrics;
        }
コード例 #17
0
ファイル: Metrics.cs プロジェクト: notator/Moritz
        /// <summary>
        /// If the previousMetrics overlaps this metrics vertically, and this.Left is les than 
        /// than previousMetrics.Right, previousMetrics.Right - this.Left is returned. 
        /// If there is no vertical overlap, this or the previousMetrics is a RestMetrics, and
        /// the metrics overlap, half the width of the rest is returned.
        /// Otherwise float.MinValue is returned.
        /// </summary>
        public float OverlapWidth(Metrics previousMetrics)
        {
            bool verticalOverlap = true;
            if(!(this is BarlineMetrics))
            {
                if((previousMetrics.Top > Bottom) || (previousMetrics.Bottom < Top))
                    verticalOverlap = false;
            }

            float overlap = float.MinValue;
            if(verticalOverlap && previousMetrics.Right > Left)
            {
                overlap = previousMetrics.Right - this.Left;
                if(this._objectType == "b" || this._objectType == "n")
                {
                    overlap -= ((this.Right - this.Left) * 0.15F);
                    overlap = overlap > 0F ? overlap : 0F;
                }
            }

            if(!verticalOverlap && this is RestMetrics && this.OriginX <= previousMetrics.Right)
                overlap = previousMetrics.Right - this.OriginX;

            if(!verticalOverlap && previousMetrics is RestMetrics && previousMetrics.OriginX >= Left)
                overlap = previousMetrics.OriginX - Left;

            return overlap;
        }
コード例 #18
0
ファイル: ChordMetrics.cs プロジェクト: notator/Moritz
        /// <summary>
        /// The flagBlock is moved to the correct x-position wrt noteheads.
        /// The distance between the inner notehead's alignmentY and the closest edge of the flagsBlock is set to a little less than two spaces.
        /// If there is only one flag, the distance is increased to a little less than three spaces (1 octave).
        /// If the stem is up and the bottom of the flagBlock is too low, the flagBlock is moved up.
        /// If the stem is down and the top of the flagBlock is too high, the flagBlock is moved down.
        /// </summary>
        private void SetFlagsPositionReNoteheads(List<HeadMetrics> topDownHeadsMetrics, Metrics flagsBlockMetrics, VerticalDir stemDirection, float stemThickness)
        {
            Debug.Assert(flagsBlockMetrics != null);

            HeadMetrics outerNoteheadMetrics = FindOuterNotehead(topDownHeadsMetrics, stemDirection);
            HeadMetrics innerNoteheadMetrics = FindInnerNotehead(topDownHeadsMetrics, stemDirection);
            float innerNoteheadAlignmentY = (innerNoteheadMetrics.Bottom + innerNoteheadMetrics.Top) / 2F;
            float minDist = _gap * 1.8F; // constant found by experiment
            float deltaX = 0;
            float deltaY = 0;
            if(stemDirection == VerticalDir.up)
            {
                deltaY = minDist - (innerNoteheadAlignmentY - flagsBlockMetrics.Bottom);
                if(flagsBlockMetrics.ID_Type == "Right1Flag")
                    deltaY += _gap;
                deltaY *= -1;

                if(flagsBlockMetrics.ID_Type == "Right1Flag")
                {
                    if((flagsBlockMetrics.Bottom + deltaY) > (_gap * 2.5F))
                    {
                        deltaY = (_gap * 2.5F) - flagsBlockMetrics.Bottom;
                    }
                }
                else // other right flag types
                    if((flagsBlockMetrics.Bottom + deltaY) > (_gap * 3.5F))
                    {
                        deltaY = (_gap * 3.5F) - flagsBlockMetrics.Bottom;
                    }

                deltaX = outerNoteheadMetrics.RightStemX - (stemThickness / 2F);
            }
            else // stem is down
            {
                deltaY = minDist - (flagsBlockMetrics.Top - innerNoteheadAlignmentY);
                if(flagsBlockMetrics.ID_Type == "Left1Flag")
                    deltaY += _gap;

                if(flagsBlockMetrics.ID_Type == "Left1Flag")
                {
                    if((flagsBlockMetrics.Top + deltaY) < (_gap * 1.5F))
                    {
                        deltaY = (_gap * 1.5F) - flagsBlockMetrics.Top;
                    }
                }
                else // other left flag types
                    if((flagsBlockMetrics.Top + deltaY) < (_gap * 0.5F))
                    {
                        deltaY = (_gap * 0.5F) - flagsBlockMetrics.Top;
                    }

                deltaX = outerNoteheadMetrics.LeftStemX + (stemThickness / 2F);
            }
            flagsBlockMetrics.Move(deltaX, deltaY);
            // the flagsBlockMetrics is added to either this.MetricsList or this.PostJustificationMetrics in a later function.
        }
コード例 #19
0
ファイル: Metrics.cs プロジェクト: notator/Moritz
 /// <summary>
 /// Adds the metrics to the MetricsList and includes it in this object's boundary.
 /// The boundary is used for collision checking. All objects which should move together with this object
 /// must be added to the MetricsList.
 /// </summary>
 /// <param name="metrics"></param>
 public virtual void Add(Metrics metrics)
 {
     MetricsList.Add(metrics);
     ResetBoundary();
 }
コード例 #20
0
ファイル: ChordMetrics.cs プロジェクト: notator/Moritz
 /// <summary>
 /// returns float.MinValue if no overlap is found
 /// </summary>
 /// <param name="followingMetrics"></param>
 /// <returns></returns>
 public new float OverlapWidth(Metrics followingMetrics)
 {
     float maxOverlapWidth = float.MinValue;
     float overlap = float.MinValue;
     #region _stemMetrics
     if(_stemMetrics != null)
     {
         overlap = followingMetrics.OverlapWidth(_stemMetrics);
         maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap;
     }
     #endregion
     #region _flagsBlockMetrics
     if(_flagsBlockMetrics != null)
     {
         overlap = followingMetrics.OverlapWidth(_flagsBlockMetrics);
         maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap;
     }
     #endregion
     #region _topDownHeadsMetrics
     if(_headsMetricsTopDown != null)
     {
         foreach(HeadMetrics headMetric in _headsMetricsTopDown)
         {
             overlap = followingMetrics.OverlapWidth(headMetric);
             maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap;
         }
     }
     #endregion
     #region _accidentalsMetrics
     if(_topDownAccidentalsMetrics != null)
     {
         foreach(AccidentalMetrics accidentalMetric in _topDownAccidentalsMetrics)
         {
             overlap = followingMetrics.OverlapWidth(accidentalMetric);
             maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap;
         }
     }
     #endregion
     #region ledgerlineBlocksMetrics
     if(_upperLedgerlineBlockMetrics != null)
     {
         overlap = followingMetrics.OverlapWidth(_upperLedgerlineBlockMetrics);
         maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap;
     }
     if(_lowerLedgerlineBlockMetrics != null)
     {
         overlap = followingMetrics.OverlapWidth(_lowerLedgerlineBlockMetrics);
         maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap;
     }
     #endregion
     #region
     if(_cautionaryBracketsMetrics != null)
     {
         foreach(CautionaryBracketMetrics cautionaryBracketMetrics in _cautionaryBracketsMetrics)
         {
             overlap = followingMetrics.OverlapWidth(cautionaryBracketMetrics);
             maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap;
         }
     }
     #endregion
     #region _ornamentMetrics
     if(_ornamentMetrics != null)
     {
         overlap = followingMetrics.OverlapWidth(_ornamentMetrics);
         maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap;
     }
     #endregion
     #region _lyricMetrics
     if(_lyricMetrics != null)
     {
         overlap = followingMetrics.OverlapWidth(_lyricMetrics);
         maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap;
     }
     #endregion
     #region _dynamicMetrics
     if(_dynamicMetrics != null)
     {
         overlap = followingMetrics.OverlapWidth(_dynamicMetrics);
         maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap;
     }
     #endregion
     #region NoteheadExtendersMetrics
     // NoteheadExtenders should only be created after JustifyHorizontally(),
     // so they should be null here.
     Debug.Assert(NoteheadExtendersMetrics == null);
     #endregion
     return maxOverlapWidth;
 }
コード例 #21
0
        /// <summary>
        /// noteObjects[noteObjectIndex] is the first OutputChordSymbol or OutputRestSymbol in the tuplet, and is the
        /// noteObject to which the tuplet is attached,
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="tupletDef"></param>
        /// <param name="noteObjects"></param>
        /// <param name="noteObjectIndex"></param>
        public Tuplet(Graphics graphics, TupletDef tupletDef, List <NoteObject> noteObjects, int noteObjectIndex)
            : base(noteObjects[noteObjectIndex])
        {
            List <NoteObject> tupletChordsAndRests = GetTupletChordsAndRests(noteObjects, noteObjectIndex, tupletDef);

            var gap      = M.PageFormat.GapVBPX;
            var textInfo = GetTupletTextInfo(tupletDef.InnerDuration, tupletDef.OuterDuration);

            Metrics = new TextMetrics(CSSObjectClass.tupletText, graphics, textInfo);

            var    textHeight = (Metrics.Bottom - Metrics.Top);
            var    textWidth  = (Metrics.Right - Metrics.Left);
            bool   isOver     = (tupletDef.Orient == Orientation.up);
            double textXAlignment;

            if (tupletChordsAndRests.Count > 2)
            {
                int     alignedIndex = (int)(tupletChordsAndRests.Count) / 2;
                Metrics metrics      = tupletChordsAndRests[alignedIndex].Metrics;
                if (metrics is ChordMetrics cMetrics)
                {
                    textXAlignment = cMetrics.OriginX - textWidth / 4;
                }
                else
                {
                    textXAlignment = ((metrics.Right - metrics.Left) / 2) + metrics.Left;
                }
            }
            else
            {
                M.Assert(tupletChordsAndRests.Count == 2);
                Metrics metrics1 = tupletChordsAndRests[0].Metrics;
                Metrics metrics2 = tupletChordsAndRests[1].Metrics;

                textXAlignment = ((metrics1.Left + metrics2.Right) / 2) - textWidth / 4;
            }

            //textYAlignment = (isOver) ? metrics.Top - gap - (textHeight / 2) : metrics.Bottom + gap + (textHeight / 2);
            double textYAlignment = (isOver) ? double.MaxValue : double.MinValue;;

            foreach (NoteObject noteObject in tupletChordsAndRests)
            {
                var metrics = noteObject.Metrics;
                if (isOver)
                {
                    textYAlignment = (metrics.Top < textYAlignment) ? metrics.Top : textYAlignment;
                }
                else
                {
                    textYAlignment = (metrics.Bottom > textYAlignment) ? metrics.Bottom : textYAlignment;
                }
            }

            #region move vertically off the staff if necessary
            StaffMetrics staffMetrics = tupletChordsAndRests[0].Voice.Staff.Metrics;
            if (isOver)
            {
                double topMax = staffMetrics.StafflinesTop - gap - (textHeight / 2);
                if (textYAlignment > topMax)
                {
                    textYAlignment = topMax;
                }
            }
            else
            {
                double topMin = staffMetrics.StafflinesBottom + gap + (textHeight / 2);
                if (textYAlignment < topMin)
                {
                    textYAlignment = topMin;
                }
            }
            #endregion

            Metrics.Move(textXAlignment, textYAlignment + (textHeight / 2));

            // set auto correctly later -- depends on beaming
            if (tupletDef.Bracket == TupletBracketDisplay.yes || tupletDef.Bracket == TupletBracketDisplay.auto)
            {
                double bracketHoriz  = textYAlignment;
                double bracketLeft   = tupletChordsAndRests[0].Metrics.Left - M.PageFormat.StafflineStemStrokeWidthVBPX;
                double bracketRight  = tupletChordsAndRests[tupletChordsAndRests.Count - 1].Metrics.Right + M.PageFormat.StafflineStemStrokeWidthVBPX;
                double bracketHeight = gap * 0.75;

                _textAndBracketMetrics = new TupletMetrics((TextMetrics)Metrics, bracketHoriz, bracketLeft, bracketRight, bracketHeight, isOver);
            }
        }