Example #1
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);
            }
        }
Example #2
0
        /// <summary>
        /// Used when creating temporary heads for chord alignment purposes.
        /// </summary>
        public HeadMetrics(HeadMetrics otherHead, DurationClass durationClass)
            : base(durationClass, false, otherHead.FontHeight)
        {
            // move to position of other head
            Move(otherHead.OriginX - _originX, otherHead.OriginY - OriginY);

            float horizontalPadding = otherHead.FontHeight * 0.04F;

            _leftStemX  = _left;
            _rightStemX = _right;
            _left      -= horizontalPadding;
            _right     += horizontalPadding;
        }
Example #3
0
        /// <summary>
        /// Used when creating temporary heads for chord alignment purposes.
        /// </summary>
        public HeadMetrics(HeadMetrics otherHead, DurationClass durationClass)
            : base(durationClass, otherHead.FontHeight, CSSObjectClass.none)
        {
            // move to position of other head
            Move(otherHead.OriginX - _originX, otherHead.OriginY - OriginY);

            double horizontalPadding = otherHead.FontHeight * 0.04;

            _leftStemX  = _left;
            _rightStemX = _right;
            _left      -= horizontalPadding;
            _right     += horizontalPadding;
        }
Example #4
0
        /// <summary>
        /// Notehead metrics.Left and metrics.Right include horizontal padding,
        /// so head overlaps cannot be checked using the standard Metrics.Overlaps function.
        /// </summary>
        public bool OverlapsHead(HeadMetrics otherHeadMetrics)
        {
            // See the above constructor. Sorry, I didnt want to save the value in every Head!
            float thisHorizontalPadding = this._fontHeight * 0.04F;
            float thisRealLeft          = _left + thisHorizontalPadding;
            float thisRealRight         = _right - thisHorizontalPadding;

            float otherHorizontalPadding = otherHeadMetrics.FontHeight * 0.04F;
            float otherRealLeft          = otherHeadMetrics.Left + thisHorizontalPadding;
            float otherRealRight         = otherHeadMetrics.Right - thisHorizontalPadding;

            bool verticalOverlap   = this.Bottom >= otherHeadMetrics.Top && this.Top <= otherHeadMetrics.Bottom;
            bool horizontalOverlap = thisRealRight >= otherRealLeft && thisRealLeft <= otherRealRight;

            return(verticalOverlap && horizontalOverlap);
        }
Example #5
0
        /// <summary>
        /// The distance between the inner edge of this beamBlock and the headmetrics.
        /// This is a positive value
        ///     a) if the _stemDirection is VerticalDir.up and the headMetrics is completely below this beamBlock,
        /// or  b) if the _stemDirection is VerticalDir.down and the headMetrics is completely above this beamBlock.
        /// </summary>
        private float VerticalDistanceToHead(HeadMetrics headMetrics, float headMsPosition)
        {
            float headX             = headMetrics.OriginX;
            float headY             = headMetrics.Top;
            float minDistanceToHead = float.MaxValue;
            float tanA = this.TanAngle;

            if (_stemDirection == VerticalDir.up)
            {
                float beamBottomAtHeadY;
                foreach (Beam beam in Beams)
                {
                    float beamBeginMsPosition = BeamBeginMsPosition(beam);
                    float beamEndMsPosition   = BeamEndMsPosition(beam);
                    if (beamBeginMsPosition <= headMsPosition && beamEndMsPosition >= headMsPosition)
                    {
                        beamBottomAtHeadY = beam.LeftTopY - ((headX - beam.LeftX) * tanA) + _beamThickness;
                        float distanceToHead = headY - beamBottomAtHeadY;
                        minDistanceToHead = minDistanceToHead < distanceToHead ? minDistanceToHead : distanceToHead;
                    }
                }
            }
            else // _stemDirection == VerticalDir.down
            {
                headY = headMetrics.Bottom;
                float beamTopAtHeadY;
                foreach (Beam beam in Beams)
                {
                    float beamBeginMsPosition = BeamBeginMsPosition(beam);
                    float beamEndMsPosition   = BeamEndMsPosition(beam);
                    if (beamBeginMsPosition <= headMsPosition && beamEndMsPosition >= headMsPosition)
                    {
                        beamTopAtHeadY = beam.LeftTopY - ((headX - beam.LeftX) * tanA);
                        float distanceToHead = beamTopAtHeadY - headY;
                        minDistanceToHead = minDistanceToHead < distanceToHead ? minDistanceToHead : distanceToHead;
                    }
                }
            }
            return(minDistanceToHead);
        }
Example #6
0
 /// <summary>
 /// The distance between the inner edge of this beamBlock and the headmetrics.
 /// This is a positive value
 ///     a) if the _stemDirection is VerticalDir.up and the headMetrics is completely below this beamBlock,
 /// or  b) if the _stemDirection is VerticalDir.down and the headMetrics is completely above this beamBlock.
 /// </summary>
 private float VerticalDistanceToHead(HeadMetrics headMetrics, float headMsPosition)
 {
     float headX = headMetrics.OriginX;
     float headY = headMetrics.Top;
     float minDistanceToHead = float.MaxValue;
     float tanA = this.TanAngle;
     if(_stemDirection == VerticalDir.up)
     {
         float beamBottomAtHeadY;
         foreach(Beam beam in Beams)
         {
             float beamBeginMsPosition = BeamBeginMsPosition(beam);
             float beamEndMsPosition = BeamEndMsPosition(beam);
             if(beamBeginMsPosition <= headMsPosition && beamEndMsPosition >= headMsPosition)
             {
                 beamBottomAtHeadY = beam.LeftTopY - ((headX - beam.LeftX) * tanA) + _beamThickness;
                 float distanceToHead = headY - beamBottomAtHeadY;
                 minDistanceToHead = minDistanceToHead < distanceToHead ? minDistanceToHead : distanceToHead;
             }
         }
     }
     else // _stemDirection == VerticalDir.down
     {
         headY = headMetrics.Bottom;
         float beamTopAtHeadY;
         foreach(Beam beam in Beams)
         {
             float beamBeginMsPosition = BeamBeginMsPosition(beam);
             float beamEndMsPosition = BeamEndMsPosition(beam);
             if(beamBeginMsPosition <= headMsPosition && beamEndMsPosition >= headMsPosition)
             {
                 beamTopAtHeadY = beam.LeftTopY - ((headX - beam.LeftX) * tanA);
                 float distanceToHead = beamTopAtHeadY - headY;
                 minDistanceToHead = minDistanceToHead < distanceToHead ? minDistanceToHead : distanceToHead;
             }
         }
     }
     return minDistanceToHead;
 }
Example #7
0
        /// <summary>
        /// chord.Heads are in top-down order.
        /// </summary>
        private void SetHeadsMetrics(ChordSymbol chord, float ledgerlineStemStrokeWidth)
        {
            _headsMetricsTopDown = new List<HeadMetrics>();

            HeadMetrics hMetrics = new HeadMetrics(chord, null, _gap); // the head is horizontally aligned at 0 by default.
            float horizontalShift = hMetrics.RightStemX - hMetrics.LeftStemX - (ledgerlineStemStrokeWidth / 2F); // the distance to shift left or right if heads would collide
            float shiftRange = _gap * 0.75F;

            if(chord.Stem.Direction == VerticalDir.up)
            {
                List<Head> bottomUpHeads = new List<Head>();
                foreach(Head head in chord.HeadsTopDown)
                    bottomUpHeads.Insert(0, head);
                List<HeadMetrics> bottomUpMetrics = new List<HeadMetrics>();

                foreach(Head head in bottomUpHeads)
                {
                    float newHeadOriginY = head.GetOriginY(_clef, _gap); // note that the CHORD's originY is always at the top line of the staff
                    float newHeadAlignX = 0F;
                    foreach(Metrics headMetric in bottomUpMetrics)
                    {
                        float existingHeadAlignX = (headMetric.Left + headMetric.Right) / 2F;
                        if((newHeadOriginY == headMetric.OriginY)
                        || (existingHeadAlignX == 0F
                            && newHeadAlignX < (existingHeadAlignX + horizontalShift)
                            && newHeadOriginY > (headMetric.OriginY - shiftRange)))
                        {
                            newHeadAlignX = existingHeadAlignX + horizontalShift; // shifts more than once for extreme clusters ( e.g. F,F#,G)
                        }
                        else
                            newHeadAlignX = 0;
                    }

                    HeadMetrics headMetrics = new HeadMetrics(chord, head, _gap);
                    headMetrics.Move(newHeadAlignX, newHeadOriginY); // moves head.originY to headY
                    bottomUpMetrics.Add(headMetrics);
                }
                for(int i = bottomUpMetrics.Count - 1; i >= 0; --i)
                {
                    _headsMetricsTopDown.Add(bottomUpMetrics[i]);
                }
            }
            else // stem is down
            {
                foreach(Head head in chord.HeadsTopDown)
                {
                    float newHeadOriginY = head.GetOriginY(_clef, _gap); // note that the CHORD's originY is always at the top line of the staff
                    float newHeadAlignX = 0F;
                    foreach(HeadMetrics headMetric in _headsMetricsTopDown)
                    {
                        float existingHeadAlignX = (headMetric.Left + headMetric.Right) / 2F;
                        if((newHeadOriginY == headMetric.OriginY)
                        || (existingHeadAlignX == 0F
                            && newHeadAlignX < (existingHeadAlignX + horizontalShift)
                            && newHeadOriginY < (headMetric.OriginY + shiftRange)))
                        {
                            newHeadAlignX -= horizontalShift; // can shift left more than once
                        }
                        else
                            newHeadAlignX = 0;
                    }

                    HeadMetrics headMetrics = new HeadMetrics(chord, head, _gap);
                    headMetrics.Move(newHeadAlignX, newHeadOriginY); // moves head.originY to headY
                    _headsMetricsTopDown.Add(headMetrics);
                }
            }

            Debug.Assert(_originX == 0F);
            Debug.Assert(_headsMetricsTopDown.Count == chord.HeadsTopDown.Count);
        }
Example #8
0
        /// <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);
        }
Example #9
0
        /// <summary>
        /// Notehead metrics.Left and metrics.Right include horizontal padding,
        /// so head overlaps cannot be checked using the standard Metrics.Overlaps function.
        /// </summary>
        public bool OverlapsHead(HeadMetrics otherHeadMetrics)
        {
            // See the above constructor. Sorry, I didnt want to save the value in every Head!
            float thisHorizontalPadding = this._fontHeight * 0.04F;
            float thisRealLeft = _left + thisHorizontalPadding;
            float thisRealRight = _right - thisHorizontalPadding;

            float otherHorizontalPadding = otherHeadMetrics.FontHeight * 0.04F;
            float otherRealLeft = otherHeadMetrics.Left + thisHorizontalPadding;
            float otherRealRight = otherHeadMetrics.Right - thisHorizontalPadding;

            bool verticalOverlap = this.Bottom >= otherHeadMetrics.Top && this.Top <= otherHeadMetrics.Bottom;
            bool horizontalOverlap = thisRealRight >= otherRealLeft && thisRealLeft <= otherRealRight;

            return verticalOverlap && horizontalOverlap;
        }
Example #10
0
        /// <summary>
        /// Used when creating temporary heads for chord alignment purposes.
        /// </summary>
        public HeadMetrics(HeadMetrics otherHead, DurationClass durationClass)
            : base(durationClass, false, otherHead.FontHeight)
        {
            // move to position of other head
            Move(otherHead.OriginX - _originX, otherHead.OriginY - OriginY);

            float horizontalPadding = otherHead.FontHeight * 0.04F;
            _leftStemX = _left;
            _rightStemX = _right;
            _left -= horizontalPadding;
            _right += horizontalPadding;
        }