Esempio n. 1
0
        private void MoveStemTips()
        {
            float      staffOriginY = Chords[0].Voice.Staff.Metrics.OriginY;
            QuaverBeam quaverBeam   = null;

            foreach (Beam beam in Beams)
            {
                quaverBeam = beam as QuaverBeam;
                if (quaverBeam != null)
                {
                    break;
                }
            }
            Debug.Assert(quaverBeam != null);
            float tanAlpha = (quaverBeam.RightTopY - quaverBeam.LeftTopY) / (quaverBeam.RightX - quaverBeam.LeftX);

            foreach (ChordSymbol chord in Chords)
            {
                ChordMetrics chordMetrics = ((ChordMetrics)chord.Metrics);
                StemMetrics  stemMetrics  = chordMetrics.StemMetrics; // a clone

                Debug.Assert(chord.Stem.Direction == _stemDirection); // just to be sure.

                float stemTipDeltaY = ((stemMetrics.OriginX - this._left) * tanAlpha);
                float stemTipY      = quaverBeam.LeftTopY + stemTipDeltaY;
                chordMetrics.MoveOuterStemTip(stemTipY, _stemDirection); // dont just move the clone! Moves the auxilliaries too.
            }
        }
Esempio n. 2
0
        private List <float> GetX1sFromChord1(ChordMetrics chord1Metrics, float hairlinePadding)
        {
            List <float>           x1s = new List <float>();
            LedgerlineBlockMetrics upperLedgerlineMetrics = chord1Metrics.UpperLedgerlineBlockMetrics;
            LedgerlineBlockMetrics lowerLedgerlineMetrics = chord1Metrics.LowerLedgerlineBlockMetrics;
            List <HeadMetrics>     headsMetrics           = chord1Metrics.HeadsMetrics;

            Debug.Assert(headsMetrics.Count > 0);

            foreach (HeadMetrics headmetrics in headsMetrics)
            {
                float x1 = headmetrics.Right;
                if (upperLedgerlineMetrics != null &&
                    headmetrics.OriginY >= upperLedgerlineMetrics.Top && headmetrics.OriginY <= upperLedgerlineMetrics.Bottom)
                {
                    x1 = upperLedgerlineMetrics.Right;
                }
                if (lowerLedgerlineMetrics != null &&
                    headmetrics.OriginY >= lowerLedgerlineMetrics.Top && headmetrics.OriginY <= lowerLedgerlineMetrics.Bottom)
                {
                    x1 = lowerLedgerlineMetrics.Right;
                }

                x1s.Add(x1 + hairlinePadding);
            }
            return(x1s);
        }
Esempio n. 3
0
        public override void WriteSVG(SvgWriter w, bool staffIsVisible)
        {
            if (ChordMetrics.BeamBlock != null)
            {
                ChordMetrics.BeamBlock.WriteSVG(w);
            }

            w.SvgStartGroup("inputChord", "inputChord" + SvgScore.UniqueID_Number);

            Debug.Assert(_msDuration > 0);

            if (staffIsVisible)
            {
                w.WriteAttributeString("score", "alignmentX", null, ChordMetrics.OriginX.ToString(M.En_USNumberFormat));
            }
            w.WriteAttributeString("score", "msDuration", null, _msDuration.ToString());

            _inputChordDef.WriteSvg(w);

            w.SvgStartGroup(null, "graphics" + SvgScore.UniqueID_Number);
            ChordMetrics.WriteSVG(w);
            w.SvgEndGroup();

            w.SvgEndGroup();
        }
Esempio n. 4
0
        private List <Beam> CreateBeams(DurationClass durationClass, float stubWidth)
        {
            List <Beam> newBeams  = new List <Beam>();
            bool        inBeam    = false;
            float       beamLeft  = -1F;
            float       beamRight = -1F;

            ChordMetrics rightMostChordMetrics = (ChordMetrics)Chords[Chords.Count - 1].Metrics;
            float        rightMostStemX        = rightMostChordMetrics.StemMetrics.OriginX;

            int stemNumber = 1;

            foreach (ChordSymbol chord in Chords)
            {
                ChordMetrics chordMetrics = (ChordMetrics)chord.Metrics;
                float        stemX        = chordMetrics.StemMetrics.OriginX;

                bool hasLessThanOrEqualBeams = HasLessThanOrEqualBeams(durationClass, chord.DurationClass);
                if (!inBeam && hasLessThanOrEqualBeams)
                {
                    beamLeft  = stemX;
                    beamRight = stemX;
                    inBeam    = true;
                }
                else if (inBeam && hasLessThanOrEqualBeams)
                {
                    beamRight = stemX;
                }

                if (inBeam && ((!hasLessThanOrEqualBeams) || stemX == rightMostStemX)) // different durationClass or end of beamBlock
                {
                    bool isStub = false;
                    if (beamLeft == beamRight)
                    {
                        // Create a beamStub. All beam stubs are initially to the left of the stem to which they are attached.
                        // Beam stubs attached to the first stem in a beamed group are replaced later in this function.
                        beamLeft -= stubWidth;
                        isStub    = true;
                    }

                    Beam newBeam = NewBeam(durationClass, beamLeft, beamRight, isStub);
                    newBeams.Add(newBeam);
                    inBeam = false;
                }
                stemNumber++;
            }

            ChordMetrics leftMostChordMetrics = (ChordMetrics)Chords[0].Metrics;
            float        leftMostStemX        = leftMostChordMetrics.StemMetrics.OriginX;

            if (newBeams.Count > 0 && newBeams[0].LeftX < leftMostStemX && newBeams[0].RightX == leftMostStemX)
            {
                // Replace a beamStub on the left of the first stem by a beam stub on the right.
                Beam newBeam = NewBeam(durationClass, leftMostStemX, leftMostStemX + stubWidth, true);
                newBeams.RemoveAt(0);
                newBeams.Insert(0, newBeam);
            }

            return(newBeams);
        }
Esempio n. 5
0
        /// <summary>
        /// Moves this horizontal beamBlock vertically until it is on the right side (above or below)
        /// the noteheads, and as close as possible to the noteheads.
        /// If there is only a single (quaver) beam, it ends up with its inner edge one octave from
        /// the OriginY of the closest notehead in the group.
        /// Otherwise the smallest distance between any beam and the closest notehead will be a sixth.
        /// </summary>
        /// <returns></returns>
        private void MoveToNoteheads(List <ChordMetrics> chordsMetrics, Dictionary <DurationClass, float> durationClassBeamThickness)
        {
            float staffOriginY = Chords[0].Voice.Staff.Metrics.OriginY;

            if (_stemDirection == VerticalDir.down)
            {
                float lowestBottom = float.MinValue;
                for (int i = 0; i < Chords.Count; ++i)
                {
                    ChordMetrics chordMetrics      = chordsMetrics[i];
                    HeadMetrics  bottomHeadMetrics = chordMetrics.BottomHeadMetrics;
                    float        beamBottom        =
                        bottomHeadMetrics.OriginY + durationClassBeamThickness[Chords[i].DurationClass];
                    if (((bottomHeadMetrics.OriginY - staffOriginY) % _gap) != 0)
                    {
                        beamBottom += (_gap * 2.5F);
                    }
                    else
                    {
                        beamBottom += (_gap * 2.65F);
                    }

                    lowestBottom = lowestBottom > beamBottom ? lowestBottom : beamBottom;
                }
                if (Beams.Count == 1) // only a quaver beam
                {
                    lowestBottom += _gap;
                }

                Move(lowestBottom - staffOriginY);
            }
            else // stems up
            {
                float highestTop = float.MaxValue;
                for (int i = 0; i < Chords.Count; ++i)
                {
                    ChordMetrics chordMetrics   = chordsMetrics[i];
                    HeadMetrics  topHeadMetrics = chordMetrics.TopHeadMetrics;
                    float        beamTop        =
                        topHeadMetrics.OriginY - durationClassBeamThickness[Chords[i].DurationClass];
                    if (((topHeadMetrics.OriginY - staffOriginY) % _gap) != 0)
                    {
                        beamTop -= (_gap * 2.5F);
                    }
                    else
                    {
                        beamTop -= (_gap * 2.65F);
                    }

                    highestTop = highestTop < beamTop ? highestTop : beamTop;
                }
                if (Beams.Count == 1) // only a quaver beam
                {
                    highestTop -= _gap;
                }

                Move(highestTop - staffOriginY);
            }
        }
Esempio n. 6
0
        internal void AddSlurTemplate(double slurBeginX, double slurBeginY, double slurEndX, double slurEndY, double gap, bool isOver, double endAngle)
        {
            // The SVG scale is such that there is no problem using integers here.

            double dyControl = gap * 3;
            double dxControl = dyControl / Math.Tan(endAngle);

            double x1 = slurBeginX;
            double y1 = slurBeginY;
            double x4 = slurEndX;
            double y4 = slurEndY;

            SlurTemplate slurTemplate      = null;
            double       shortSlurMaxWidth = gap * 20;  // 5 staff heights
            double       shortSlurMinWidth = gap * 9.5; // 4.4 is shortTieMaxWidth
            double       width             = x4 - x1;

            if (width <= shortSlurMaxWidth)
            {
                if (width <= shortSlurMinWidth)
                {
                    double factor = width / shortSlurMinWidth;
                    dxControl *= factor;
                    dyControl *= factor;
                }
                // short (=two-point) slur template
                // standard Bezier points
                var p1 = new Point((int)x1, (int)y1);
                var p2 = (isOver) ? new Point((int)(x1 + dxControl), (int)(y1 - dyControl)) : new Point((int)(x1 + dxControl), (int)(y1 + dyControl));
                var p4 = new Point((int)x4, (int)y4);
                var p3 = (isOver) ? new Point((int)(x4 - dxControl), (int)(y4 - dyControl)) : new Point((int)(x4 - dxControl), (int)(y4 + dyControl));

                slurTemplate = new SlurTemplate(p1, p2, p3, p4, gap, isOver);
            }
            else
            {
                // long (=three-point) slur template
                var p1 = new Point((int)x1, (int)y1);
                var c1 = (isOver) ? new Point((int)(x1 + dxControl), (int)(y1 - dyControl)) : new Point((int)(x1 + dxControl), (int)(y1 + dyControl));
                var p3 = new Point((int)x4, (int)y4);
                var c3 = (isOver) ? new Point((int)(x4 - dxControl), (int)(y4 - dyControl)) : new Point((int)(x4 - dxControl), (int)(y4 + dyControl));
                var p2 = new Point((int)((x1 + x4) / 2), (int)((y1 + c1.Y) / 2));
                var c2 = new Point((p1.X + p2.X) / 2, p2.Y);

                slurTemplate = new SlurTemplate(p1, c1, c2, p2, c3, p3, gap, isOver);
            }

            ChordMetrics.SlurTemplates.Add(slurTemplate);                         // So that the slurTemplate will be written to SVG.
            ChordMetrics.AddSlurTieMetrics((SlurTieMetrics)slurTemplate.Metrics); // So that the tie will be moved vertically with the system.
        }
Esempio n. 7
0
        /// <summary>
        /// Returns the maximum (positive) horizontal distance by which this anchorage symbol overlaps
        /// (any characters in) the previous noteObjectMoment (which contains symbols from both voices
        /// in a 2-voice staff).
        /// This function is used by rests and barlines.It is overridden by chords.
        /// </summary>
        /// <param name="previousAS"></param>
        public override double OverlapWidth(NoteObjectMoment previousNOM)
        {
            double overlap      = double.MinValue;
            double localOverlap = 0;

            foreach (Anchor previousAS in previousNOM.Anchors)
            {
                //if(this is Study2b2ChordSymbol)
                //	localOverlap = Metrics.OverlapWidth(previousAS);
                //else
                localOverlap = ChordMetrics.OverlapWidth(previousAS);

                overlap = overlap > localOverlap ? overlap : localOverlap;
            }
            return(overlap);
        }
Esempio n. 8
0
        /// <summary>
        /// Returns the maximum (positive) horizontal distance by which this anchorage symbol overlaps
        /// (any characters in) the previous noteObjectMoment (which contains symbols from both voices
        /// in a 2-voice staff).
        /// This function is used by rests and barlines.It is overridden by chords.
        /// </summary>
        /// <param name="previousAS"></param>
        public override float OverlapWidth(NoteObjectMoment previousNOM)
        {
            float overlap      = float.MinValue;
            float localOverlap = 0F;

            foreach (AnchorageSymbol previousAS in previousNOM.AnchorageSymbols)
            {
                //if(this is Study2b2ChordSymbol)
                //	localOverlap = Metrics.OverlapWidth(previousAS);
                //else
                localOverlap = ChordMetrics.OverlapWidth(previousAS);

                overlap = overlap > localOverlap ? overlap : localOverlap;
            }
            return(overlap);
        }
Esempio n. 9
0
        /// <summary>
        /// Tie templates have similar point and control point structures as a slurs, except that
        /// 1. they are both horizontal and symmetric.
        /// 2. the maximum width of a short tie is twice the width of a tie hook.
        /// 3. long slurs have three points on the slur, long ties have four points on the tie.
        /// </summary>
        /// <param name="tieTemplateBeginX"></param>
        /// <param name="tieBeginY"></param>
        /// <param name="tieTemplateEndX"></param>
        /// <param name="tieTemplateY"></param>
        /// <param name="gap"></param>
        /// <param name="isOver"></param>
        internal void AddTieTemplate(double tieTemplateBeginX, double tieTemplateEndX, double tieTemplateY, double gap, bool isOver)
        {
            // The SVG scale is such that there should be no problem using integers here.
            TieTemplate tieTemplate = null;

            double p1x = tieTemplateBeginX;
            double p1y = tieTemplateY;
            double p2x = tieTemplateEndX;
            double p2y = tieTemplateY;

            double shortTieMaxWidth     = gap * 10;
            double veryShortTieMaxWidth = gap * 3.45;
            double width = p2x - p1x;

            double deltaControl; // the x and y distance between an end-point on the template and its related control point.

            if (width <= shortTieMaxWidth)
            {
                deltaControl = (width <= veryShortTieMaxWidth) ? width / 3 : gap * 1.15;

                var p1 = new Point((int)p1x, (int)p1y);
                var c1 = (isOver) ? new Point((int)(p1x + deltaControl), (int)(p1y - deltaControl)) : new Point((int)(p1x + deltaControl), (int)(p1y + deltaControl));
                var p2 = new Point((int)p2x, (int)p2y);
                var c2 = (isOver) ? new Point((int)(p2x - deltaControl), (int)(p2y - deltaControl)) : new Point((int)(p2x - deltaControl), (int)(p2y + deltaControl));

                tieTemplate = new ShortTieTemplate(p1, c1, c2, p2, gap, isOver);
            }
            else
            {
                deltaControl = (gap * 0.85);

                // long tie template
                var p1 = new Point((int)p1x, (int)p1y);
                var c1 = (isOver) ? new Point((int)(p1x + deltaControl), (int)(p1y - deltaControl)) : new Point((int)(p1x + deltaControl), (int)(p1y + deltaControl));
                var tc = new Point(c1.X + (int)(deltaControl / 2), c1.Y);
                var tp = new Point((int)(p1x + (p2x - p1x) / 2), c1.Y);
                var c2 = (isOver) ? new Point((int)(p2x - deltaControl), (int)(p2y - deltaControl)) : new Point((int)(p2x - deltaControl), (int)(p2y + deltaControl));
                var p2 = new Point((int)p2x, (int)p2y);

                tieTemplate = new LongTieTemplate(p1, c1, tc, tp, c2, p2, gap, isOver);
            }

            ChordMetrics.TieTemplates.Add(tieTemplate);                          // So that the slurTemplate will be written to SVG.
            ChordMetrics.AddSlurTieMetrics((SlurTieMetrics)tieTemplate.Metrics); // So that the tie will be moved vertically with the system.
        }
Esempio n. 10
0
        public void WriteSVG(SvgWriter w, int channel, CarryMsgs carryMsgs, bool graphicsOnly)
        {
            if (ChordMetrics.BeamBlock != null)
            {
                ChordMetrics.BeamBlock.WriteSVG(w);
            }

            w.SvgStartGroup(CSSObjectClass.chord.ToString());             // "chord"
            if (!graphicsOnly)
            {
                w.WriteAttributeString("score", "alignment", null, ChordMetrics.OriginX.ToString(M.En_USNumberFormat));

                _midiChordDef.WriteSVG(w, channel, carryMsgs);
            }

            w.SvgStartGroup(CSSObjectClass.graphics.ToString());
            ChordMetrics.WriteSVG(w);
            w.SvgEndGroup();

            w.SvgEndGroup(); // "chord"
        }
Esempio n. 11
0
        private float ShearAngle(List <ChordMetrics> chordsMetrics)
        {
            ChordMetrics leftChordMetrics  = chordsMetrics[0];
            ChordMetrics rightChordMetrics = chordsMetrics[chordsMetrics.Count - 1];
            float        height            =
                (((rightChordMetrics.TopHeadMetrics.OriginY + rightChordMetrics.BottomHeadMetrics.OriginY) / 2)
                 - ((leftChordMetrics.TopHeadMetrics.OriginY + leftChordMetrics.BottomHeadMetrics.OriginY) / 2));

            float width    = rightChordMetrics.StemMetrics.OriginX - leftChordMetrics.StemMetrics.OriginX;
            float tanAlpha = (height / width) / 3;

            if (tanAlpha > 0.15F)
            {
                tanAlpha = 0.15F;
            }
            if (tanAlpha < -0.15F)
            {
                tanAlpha = -0.15F;
            }

            return(tanAlpha);
        }
Esempio n. 12
0
        public override Metrics NoteObjectMetrics(Graphics graphics, NoteObject noteObject, VerticalDir voiceStemDirection, float gap, float strokeWidth)
        {
            Metrics               returnMetrics         = null;
            ClefSymbol            clef                  = noteObject as ClefSymbol;
            Barline               barline               = noteObject as Barline;
            CautionaryChordSymbol cautionaryChordSymbol = noteObject as CautionaryChordSymbol;
            ChordSymbol           chord                 = noteObject as ChordSymbol;
            RestSymbol            rest                  = noteObject as RestSymbol;

            if (barline != null)
            {
                returnMetrics = new BarlineMetrics(graphics, barline, gap);
            }
            else if (clef != null)
            {
                if (clef.ClefType != "n")
                {
                    returnMetrics = new ClefMetrics(clef, gap);
                }
            }
            else if (cautionaryChordSymbol != null)
            {
                returnMetrics = new ChordMetrics(graphics, cautionaryChordSymbol, voiceStemDirection, gap, strokeWidth);
            }
            else if (chord != null)
            {
                returnMetrics = new ChordMetrics(graphics, chord, voiceStemDirection, gap, strokeWidth);
            }
            else if (rest != null)
            {
                // All rests are originally created on the centre line.
                // They are moved vertically later, if they are on a 2-Voice staff.
                returnMetrics = new RestMetrics(graphics, rest, gap, noteObject.Voice.Staff.NumberOfStafflines, strokeWidth);
            }

            return(returnMetrics);
        }
Esempio n. 13
0
        public override void WriteSVG(SvgWriter w, bool staffIsVisible)
        {
            if (staffIsVisible && ChordMetrics.BeamBlock != null)
            {
                ChordMetrics.BeamBlock.WriteSVG(w);
            }

            w.SvgStartGroup("outputChord", "outputChord" + SvgScore.UniqueID_Number);
            if (staffIsVisible)
            {
                w.WriteAttributeString("score", "alignmentX", null, ChordMetrics.OriginX.ToString(M.En_USNumberFormat));
            }

            _midiChordDef.WriteSvg(w);

            if (staffIsVisible)
            {
                w.SvgStartGroup(null, "graphics" + SvgScore.UniqueID_Number);
                ChordMetrics.WriteSVG(w);
                w.SvgEndGroup();
            }

            w.SvgEndGroup();
        }
Esempio n. 14
0
        private List <float> GetX2sFromChord2(List <float> ys, ChordMetrics chord2Metrics, float hairlinePadding)
        {
            List <float>           x2s = new List <float>();
            LedgerlineBlockMetrics c2UpperLedgerlineMetrics = chord2Metrics.UpperLedgerlineBlockMetrics;
            LedgerlineBlockMetrics c2LowerLedgerlineMetrics = chord2Metrics.LowerLedgerlineBlockMetrics;
            List <HeadMetrics>     c2headsMetrics           = chord2Metrics.HeadsMetrics;

            Debug.Assert(c2headsMetrics.Count > 0);
            List <AccidentalMetrics> c2AccidentalsMetrics = chord2Metrics.TopDownAccidentalsMetrics;

            float verticalPadding = hairlinePadding * 4.0f;

            foreach (float y in ys)
            {
                float x2 = float.MaxValue;
                if (c2UpperLedgerlineMetrics != null)
                {
                    if (y >= (c2UpperLedgerlineMetrics.Top - verticalPadding) &&
                        y <= (c2UpperLedgerlineMetrics.Bottom + verticalPadding))
                    {
                        x2 = x2 < c2UpperLedgerlineMetrics.Left ? x2 : c2UpperLedgerlineMetrics.Left;
                    }
                }
                if (c2LowerLedgerlineMetrics != null)
                {
                    if (y >= (c2LowerLedgerlineMetrics.Top - verticalPadding) &&
                        y <= (c2LowerLedgerlineMetrics.Bottom + verticalPadding))
                    {
                        x2 = x2 < c2LowerLedgerlineMetrics.Left ? x2 : c2LowerLedgerlineMetrics.Left;
                    }
                }
                foreach (HeadMetrics headMetrics in c2headsMetrics)
                {
                    if (y >= (headMetrics.Top - verticalPadding) &&
                        y <= (headMetrics.Bottom + verticalPadding))
                    {
                        x2 = x2 < headMetrics.Left ? x2 : headMetrics.Left;
                    }
                }
                foreach (AccidentalMetrics accidentalMetrics in c2AccidentalsMetrics)
                {
                    if (y >= (accidentalMetrics.Top - verticalPadding) &&
                        y <= (accidentalMetrics.Bottom + verticalPadding))
                    {
                        x2 = x2 < accidentalMetrics.Left ? x2 : accidentalMetrics.Left;
                    }
                }
                x2 = x2 < float.MaxValue ? x2 : chord2Metrics.Left;
                x2s.Add(x2 - hairlinePadding);
            }

            float minX = float.MaxValue;

            foreach (float x in x2s)
            {
                minX = minX < x ? minX : x;
            }
            List <float> x2sMinimum = new List <float>();

            foreach (float x in x2s)
            {
                x2sMinimum.Add(minX);
            }

            return(x2sMinimum);
        }
Esempio n. 15
0
 public override void AddMetricsToEdge(HorizontalEdge horizontalEdge)
 {
     ChordMetrics.AddToEdge(horizontalEdge);
 }
Esempio n. 16
0
        private void AdjustRestsForVerticalChordCollisions(int restsChannelIndex)
        {
            Debug.Assert(restsChannelIndex == 0 || restsChannelIndex == 1);

            List <NoteObject> restObjects;
            List <NoteObject> chordObjects;
            bool shiftRestUp;

            if (restsChannelIndex == 0)
            {
                shiftRestUp  = true;
                restObjects  = Voices[0].NoteObjects;
                chordObjects = Voices[1].NoteObjects;
            }
            else
            {
                shiftRestUp  = false;
                restObjects  = Voices[1].NoteObjects;
                chordObjects = Voices[0].NoteObjects;
            }

            // Move rests in the top voice up by gap increments if they are synchronous with an overlapping chord in the lower voice.
            // Move rests in the bottom voice down by gap increments if they are synchronous with an overlapping chord in the top voice.
            foreach (NoteObject restObject in restObjects)
            {
                RestSymbol restSymbol = restObject as RestSymbol;
                if (restSymbol != null)
                {
                    foreach (NoteObject chordObject in chordObjects)
                    {
                        OutputChordSymbol chordSymbol = chordObject as OutputChordSymbol;
                        if (chordSymbol != null)
                        {
                            if (chordSymbol.MsPosition > restSymbol.MsPosition)
                            {
                                break;
                            }

                            if (chordSymbol.MsPosition == restSymbol.MsPosition)
                            {
                                RestMetrics  restMetrics  = restSymbol.RestMetrics;
                                ChordMetrics chordMetrics = chordSymbol.ChordMetrics;
                                //float verticalOverlap = chordMetrics.OverlapHeight(restMetrics, Gap / 2F);
                                float verticalOverlap = chordMetrics.OverlapHeight(restMetrics, 0F);
                                if (verticalOverlap > 0)
                                {
                                    if (shiftRestUp)
                                    {
                                        //float newMaxBottom = chordMetrics.Top - Gap;
                                        float newMaxBottom = chordMetrics.Top;
                                        newMaxBottom += DurationClassDeltaAbove(restSymbol.DurationClass, Gap);
                                        while (restMetrics.Bottom > newMaxBottom)
                                        {
                                            restMetrics.LedgerlineVisible = true; // only affects breves, semibreves and minims
                                            restMetrics.Move(0F, -Gap);
                                        }
                                        break; // to next rest symbol
                                    }
                                    else
                                    {
                                        //float newMinTop = chordMetrics.Bottom + Gap;
                                        float newMinTop = chordMetrics.Bottom;
                                        newMinTop += DurationClassDeltaBelow(restSymbol.DurationClass, Gap);
                                        while (restMetrics.Top < newMinTop)
                                        {
                                            restMetrics.LedgerlineVisible = true; // only affects breves, semibreves and minims
                                            restMetrics.Move(0F, Gap);
                                        }
                                        break; // to next rest symbol
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }