private float GetDefaultStemTipY(ClefSymbol currentClef, List <ChordSymbol> chordsBeamedTogether) { float headMinTop = float.MaxValue; float headMaxBottom = float.MinValue; float gap = chordsBeamedTogether[0].Voice.Staff.Gap; int numberOfStafflines = chordsBeamedTogether[0].Voice.Staff.NumberOfStafflines; VerticalDir direction = chordsBeamedTogether[0].Stem.Direction; foreach (ChordSymbol chord in chordsBeamedTogether) { foreach (Head head in chord.HeadsTopDown) { float headY = head.GetOriginY(currentClef, gap); headMinTop = headMinTop < headY ? headMinTop : headY; headMaxBottom = headMaxBottom > headY ? headMaxBottom : headY; } } if (direction == VerticalDir.up) { return(headMinTop - (gap * numberOfStafflines)); } else { return(headMaxBottom + (gap * numberOfStafflines)); } }
public Stem(ChordSymbol chordSymbol, Stem stem) { Chord = chordSymbol; if(stem != null) { Direction = stem.Direction; } }
public ChordMetrics(System.Drawing.Graphics graphics, ChordSymbol chord, VerticalDir voiceStemDirection, float gap, float stemStrokeWidthVBPX) : base() { _top = float.MaxValue; _right = float.MinValue; _bottom = float.MinValue; _left = float.MaxValue; _drawObjects = chord.DrawObjects; _gap = gap; // The _objectType is written to the SVG file as a group name, but is otherwise not used. if(chord is CautionaryChordSymbol) _objectType = "cautionary chord"; else _objectType = "chord"; GetStaffParameters(chord); // sets _clef to the most recent clef, and _nStafflines. // For each component, find its characterID, deltaX and deltaY re the chord's origin. // The chord's x-origin is the centre of the outermost notehead. // (deltaX of the outermost notehead is 0.) // The chord's y-origin is the top line of the staff. // (deltaY of a notehead on the top line of the staff is 0.) if(chord.BeamBlock != null && chord.BeamBlock.Chords[0] == chord) this.BeamBlock = chord.BeamBlock; SetHeadsMetrics(chord, stemStrokeWidthVBPX); if(chord.Stem.Draw) // false for cautionary chords SetStemAndFlags(chord, _headsMetricsTopDown, stemStrokeWidthVBPX); // if the chord is part of a beamGroup, the stem tips are all at one height here. // These objects are created with originX and originY at 0,0 (the chord's origin). CreateLedgerlineAndAccidentalMetrics(chord.FontHeight, chord.HeadsTopDown, _headsMetricsTopDown, stemStrokeWidthVBPX); CautionaryChordSymbol cautionaryChordSymbol = chord as CautionaryChordSymbol; if(cautionaryChordSymbol != null) { CreateCautionaryBracketsMetrics(cautionaryChordSymbol); } else { bool dynamicIsBelow; bool ornamentIsBelow; _lyricMetrics = NewLyricMetrics(chord.Voice.StemDirection, graphics, gap); GetRelativePositions(chord.Voice.StemDirection, _lyricMetrics, out ornamentIsBelow, out dynamicIsBelow); _ornamentMetrics = NewOrnamentMetrics(graphics, gap, ornamentIsBelow); _dynamicMetrics = NewDynamicMetrics(gap, dynamicIsBelow); MoveAuxilliaries(chord.Stem.Direction, gap); } SetExternalBoundary(); }
public StemMetrics(double top, double x, double bottom, double strokeWidth, VerticalDir verticalDir) : base(CSSObjectClass.stem, strokeWidth, "black") { _originX = x; _originY = top; _top = top; _right = x + strokeWidth; _bottom = bottom; _left = x - strokeWidth; VerticalDir = verticalDir; StrokeWidth = strokeWidth; }
public StemMetrics(float top, float x, float bottom, float strokeWidth, VerticalDir verticalDir) : base() { _originX = x; _originY = top; _top = top; _right = x + strokeWidth; _bottom = bottom; _left = x - strokeWidth; VerticalDir = verticalDir; StrokeWidth = strokeWidth; }
/// <summary> /// 20.01.2012 N.B. Neither this function nor ChordMetrics.MoveAuxilliariesToLyricHeight() /// have been thoroughly tested yet. /// This function should align the lyrics in each voice, moving ornaments and dynamics /// which are on the same side of the staff. (Lyrics are closest to the staff.) /// </summary> public override void AlignLyrics(List <Staff> staves) { foreach (Staff staff in staves) { if (!(staff is InvisibleOutputStaff)) { for (int voiceIndex = 0; voiceIndex < staff.Voices.Count; ++voiceIndex) { VerticalDir voiceStemDirection = VerticalDir.none; if (staff.Voices.Count > 1) { // 2-Voice staff if (voiceIndex == 0) { voiceStemDirection = VerticalDir.up; // top voice } else { voiceStemDirection = VerticalDir.down; // bottom voice } } float lyricMaxTop = float.MinValue; float lyricMinBottom = float.MaxValue; foreach (ChordSymbol chordSymbol in staff.Voices[voiceIndex].ChordSymbols) { Metrics lyricMetrics = chordSymbol.ChordMetrics.LyricMetrics; if (lyricMetrics != null) { lyricMaxTop = (lyricMaxTop > lyricMetrics.Top) ? lyricMaxTop : lyricMetrics.Top; lyricMinBottom = (lyricMinBottom < lyricMetrics.Bottom) ? lyricMinBottom : lyricMetrics.Bottom; } } if (lyricMaxTop != float.MinValue) { // the voice has lyrics if (voiceStemDirection == VerticalDir.none || voiceStemDirection == VerticalDir.down) { // the lyrics are below the staff float lyricMinTop = staff.Metrics.StafflinesBottom + (staff.SVGSystem.Score.PageFormat.Gap * 1.5F); lyricMaxTop = lyricMaxTop > lyricMinTop ? lyricMaxTop : lyricMinTop; } foreach (ChordSymbol chordSymbol in staff.Voices[voiceIndex].ChordSymbols) { chordSymbol.ChordMetrics.MoveAuxilliariesToLyricHeight(voiceStemDirection, lyricMaxTop, lyricMinBottom); } } } } } }
/// <summary> /// Shifts a horizontal beam vertically to the correct position (wrt the beamBlock) for its duration class /// </summary> /// <param name="outerLeftY"></param> /// <param name="gap"></param> /// <param name="stemDirection"></param> /// <param name="beamThickness"></param> /// <param name="nGaps"></param> protected void ShiftYsForBeamBlock(double outerLeftY, double gap, VerticalDir stemDirection, double beamThickness, int nGaps) { double dy = 0; if (stemDirection == VerticalDir.down) { dy = -(beamThickness + (gap * nGaps)); } else { dy = gap * nGaps; } dy += outerLeftY - _leftTopY; MoveYs(dy, dy); }
/// <summary> /// Shifts a horizontal beam vertically to the correct position (wrt the beamBlock) for its duration class /// </summary> /// <param name="outerLeftY"></param> /// <param name="gap"></param> /// <param name="stemDirection"></param> /// <param name="beamThickness"></param> /// <param name="nGaps"></param> protected void ShiftYsForBeamBlock(float outerLeftY, float gap, VerticalDir stemDirection, float beamThickness, int nGaps) { float dy = 0F; if (stemDirection == VerticalDir.down) { dy = -(beamThickness + (gap * nGaps)); } else { dy = gap * nGaps; } dy += outerLeftY - _leftTopY; MoveYs(dy, dy); }
public BeamBlock(ClefSymbol clef, List<ChordSymbol> chordsBeamedTogether, VerticalDir voiceStemDirection, float beamThickness, float strokeThickness) : base(null, 0, 0) { Chords = new List<ChordSymbol>(chordsBeamedTogether); SetBeamedGroupStemDirection(clef, chordsBeamedTogether, voiceStemDirection); foreach(ChordSymbol chord in chordsBeamedTogether) chord.BeamBlock = this; // prevents an isolated flag from being created _gap = Chords[0].Voice.Staff.Gap; _beamSeparation = _gap; _beamThickness = beamThickness; _strokeThickness = strokeThickness; _stemDirection = Chords[0].Stem.Direction; /****************************************************************************** * Important to set stem tips to this value before justifying horizontally. * Allows collisions between the objects outside the tips (e.g. dynamics or ornaments) * to be detected correctly. */ _defaultStemTipY = GetDefaultStemTipY(clef, chordsBeamedTogether); }
public BeamBlock(ClefSymbol clef, List <ChordSymbol> chordsBeamedTogether, VerticalDir voiceStemDirection, float beamThickness, float strokeThickness) : base(null, 0, 0) { Chords = new List <ChordSymbol>(chordsBeamedTogether); SetBeamedGroupStemDirection(clef, chordsBeamedTogether, voiceStemDirection); foreach (ChordSymbol chord in chordsBeamedTogether) { chord.BeamBlock = this; // prevents an isolated flag from being created } _gap = Chords[0].Voice.Staff.Gap; _beamSeparation = _gap; _beamThickness = beamThickness; _strokeThickness = strokeThickness; _stemDirection = Chords[0].Stem.Direction; /****************************************************************************** * Important to set stem tips to this value before justifying horizontally. * Allows collisions between the objects outside the tips (e.g. dynamics or ornaments) * to be detected correctly. */ _defaultStemTipY = GetDefaultStemTipY(clef, chordsBeamedTogether); }
/// <summary> /// This algorithm follows Gardner Read when the stemDirection is "none" (i.e. not forced): /// If there were no beam, and the majority of the stems would go up, then all the stems in the beam go up. /// ji: if there are the same number of default up and default down stems, then the direction is decided by /// the most extreme notehead in the beam group. If both extremes are the same (e.g. 1 ledgeline up and down) /// then the stems are all down. /// </summary> /// <param name="currentClef"></param> /// <param name="chordsBeamedTogether"></param> private void SetBeamedGroupStemDirection(ClefSymbol currentClef, List <ChordSymbol> chordsBeamedTogether, VerticalDir voiceStemDirection) { Debug.Assert(chordsBeamedTogether.Count > 1); VerticalDir groupStemDirection = voiceStemDirection; if (voiceStemDirection == VerticalDir.none) { // here, there is only one voice in the staff, so the direction depends on the height of the noteheads. int upStems = 0; int downStems = 0; foreach (ChordSymbol chord in chordsBeamedTogether) { VerticalDir direction = chord.DefaultStemDirection(currentClef); if (direction == VerticalDir.up) { upStems++; } else { downStems++; } } if (upStems == downStems) { groupStemDirection = GetDirectionFromExtremes(currentClef, chordsBeamedTogether); } else if (upStems > downStems) { groupStemDirection = VerticalDir.up; } else { groupStemDirection = VerticalDir.down; } } foreach (ChordSymbol chord in chordsBeamedTogether) { chord.Stem.Direction = groupStemDirection; } }
/// <summary> /// N.B. This function has not been thoroughly tested yet. 20.01.2012 /// If there is a lyric in this chord: /// If voiceStemDirection is none or down, the top of the lyric is aligned to lyricTop, /// If voiceStemDirection is up, this chord is in the upper voice of a 2-voice staff, and /// the lyric is above the chord. In this case, the bottom of the lyric is aligned to lyricBottom. /// If there is a dynamic or ornament on the same side of the staff, these are also moved by /// the same amount. /// If this chord has no lyric, nothing happens. /// </summary> public void MoveAuxilliariesToLyricHeight(VerticalDir voiceStemDirection, float lyricTop, float lyricBottom) { if(_lyricMetrics != null) { bool ornamentIsBelow; bool dynamicIsBelow; GetRelativePositions(voiceStemDirection, _lyricMetrics, out ornamentIsBelow, out dynamicIsBelow); float delta = 0F; if(_lyricMetrics.IsBelow) { delta = lyricTop - _lyricMetrics.Top; delta *= 0.7F; // this line added 12.08.2015 _lyricMetrics.Move(0F, delta); if(ornamentIsBelow && _ornamentMetrics != null) _ornamentMetrics.Move(0F, delta); if(dynamicIsBelow && _dynamicMetrics != null) _dynamicMetrics.Move(0F, delta); } else // lyric is above { delta = lyricBottom - _lyricMetrics.Bottom; _lyricMetrics.Move(0F, delta); if(!ornamentIsBelow && _ornamentMetrics != null) _ornamentMetrics.Move(0F, delta); if(!dynamicIsBelow && _dynamicMetrics != null) _dynamicMetrics.Move(0F, delta); } } }
/// <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. }
public abstract Metrics NoteObjectMetrics(Graphics graphics, NoteObject noteObject, VerticalDir voiceStemDirection, double gap, PageFormat pageFormat, string currentClefType);
public abstract void ShiftYsForBeamBlock(float outerLeftY, float gap, VerticalDir stemDirection, float beamThickness);
public StemMetrics(float top, float x, float bottom, float strokeWidth, VerticalDir verticalDir) : base() { _originX = x; _originY = top; _top = top; _right = x + strokeWidth; _bottom = bottom; _left = x - strokeWidth; VerticalDir = verticalDir; StrokeWidth = strokeWidth; }
public abstract Metrics NoteObjectMetrics(Graphics graphics, NoteObject noteObject, VerticalDir voiceStemDirection, float gap, float storkeWidth);
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; CautionaryOutputChordSymbol cautionaryOutputChordSymbol = noteObject as CautionaryOutputChordSymbol; CautionaryInputChordSymbol cautionaryInputChordSymbol = noteObject as CautionaryInputChordSymbol; 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(cautionaryOutputChordSymbol != null) { returnMetrics = new ChordMetrics(graphics, cautionaryOutputChordSymbol, voiceStemDirection, gap, strokeWidth); } else if(cautionaryInputChordSymbol != null) { returnMetrics = new ChordMetrics(graphics, cautionaryInputChordSymbol, 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; }
/// <summary> /// Returns null if the durationClass does not have a flagsBlock, /// otherwise returns the metrics for the flagsBlock attached to this chord, correctly positioned wrt the noteheads. /// </summary> private FlagsBlockMetrics GetFlagsBlockMetrics(List<HeadMetrics> topDownHeadsMetrics, DurationClass durationClass, float fontSize, VerticalDir stemDirection, float stemThickness) { Debug.Assert(durationClass == DurationClass.quaver || durationClass == DurationClass.semiquaver || durationClass == DurationClass.threeFlags || durationClass == DurationClass.fourFlags || durationClass == DurationClass.fiveFlags); FlagsBlockMetrics flagsBlockMetrics = new FlagsBlockMetrics(durationClass, fontSize, stemDirection); if(flagsBlockMetrics != null) { // flagsMetrics contains a metrics for the flags block with the outermost point at OriginX=0, BaselineY=0 // Now move the flagblock so that is positioned correctly wrt the noteheads. SetFlagsPositionReNoteheads(topDownHeadsMetrics, flagsBlockMetrics, stemDirection, stemThickness); } return flagsBlockMetrics; }
/// <summary> /// Returns the notehead to which the stem is attached. /// </summary> private HeadMetrics FindOuterNotehead(List<HeadMetrics> topDownHeadsMetrics, VerticalDir stemDirection) { Debug.Assert(topDownHeadsMetrics.Count > 0); HeadMetrics outerNotehead = null; if(stemDirection == VerticalDir.up) { outerNotehead = topDownHeadsMetrics[topDownHeadsMetrics.Count - 1]; } else { outerNotehead = topDownHeadsMetrics[0]; } return outerNotehead; }
/// <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); }
public abstract void ShiftYsForBeamBlock(float outerLeftY, float gap, VerticalDir stemDirection, float beamThickness);
/// <summary> /// topBoundary is set to topStaffline /// bottomBoundary is set to bottomStaffline /// then these bounds are widened if the chord lies outside: /// If there is no stem, /// topBoundary is set to topNotehead.Top /// bottomBoundary is set to bottomNotehead.Bottom /// else if stem is up, /// topBoundardy is set to stemTopTipY or topNoteheadTop, /// bottomBoundary is set to bottomNotehead.Bottom. /// else if stem is down, /// topBoundary is set to topNotehead.Top, /// bottomBoundary is set to stemBottomTipY or bottomNoteheadBottom. /// </summary> private void GetTopAndBottomBounds(VerticalDir stemDirection, out float topBoundary, out float bottomBoundary) { float topStaffline = _clef.Voice.Staff.Metrics.StafflinesTop; topBoundary = topStaffline; // top of staff bottomBoundary = topStaffline + (_gap * 4F); // bottom of staff if(_stemMetrics == null) { float topOfTopHead = _headsMetricsTopDown[0].Top; topBoundary = (topBoundary < topOfTopHead) ? topBoundary : topOfTopHead; float bottomOfBottomHead = _headsMetricsTopDown[_headsMetricsTopDown.Count - 1].Bottom; bottomBoundary = (bottomBoundary > bottomOfBottomHead) ? bottomBoundary : bottomOfBottomHead; } else if(_stemMetrics.VerticalDir == VerticalDir.up) { topBoundary = (topBoundary < _stemMetrics.Top) ? topBoundary : _stemMetrics.Top; float bottomOfBottomHead = _headsMetricsTopDown[_headsMetricsTopDown.Count - 1].Bottom; bottomBoundary = (bottomBoundary > bottomOfBottomHead) ? bottomBoundary : bottomOfBottomHead; } else { float topOfTopHead = _headsMetricsTopDown[0].Top; topBoundary = (topBoundary < topOfTopHead) ? topBoundary : topOfTopHead; bottomBoundary = (bottomBoundary > _stemMetrics.Bottom) ? bottomBoundary : _stemMetrics.Bottom; } }
public void MoveOuterStemTip(float stemTipY, VerticalDir stemDirection) { if(stemDirection == VerticalDir.up) { _stemMetrics.SetTop(stemTipY); } else //(_stemDirection == VerticalDir.down) { _stemMetrics.SetBottom(stemTipY); } MoveAuxilliaries(stemDirection, _gap, 0F, _gap * 0.3F); SetExternalBoundary(); }
public Voice(Staff staff, Voice voice) { Staff = staff; StemDirection = voice.StemDirection; this.AppendNoteObjects(voice.NoteObjects); }
/// <summary> /// Should be called with a duration class having a flag block /// </summary> public FlagsBlockMetrics(DurationClass durationClass, float fontHeight, VerticalDir stemDirection) : base() { _left = 0F; _right = 0.31809F * fontHeight; _originX = 0F; _originY = 0F; _fontHeight = fontHeight; _stemDirection = stemDirection; float offset = 0F; switch (durationClass) { case DurationClass.quaver: if (_stemDirection == VerticalDir.up) { _objectType = "Right1Flag"; } else { _objectType = "Left1Flag"; } break; case DurationClass.semiquaver: if (_stemDirection == VerticalDir.up) { _objectType = "Right2Flags"; } else { _objectType = "Left2Flags"; } offset = 0.25F; break; case DurationClass.threeFlags: if (_stemDirection == VerticalDir.up) { _objectType = "Right3Flags"; } else { _objectType = "Left3Flags"; } offset = 0.5F; break; case DurationClass.fourFlags: if (_stemDirection == VerticalDir.up) { _objectType = "Right4Flags"; } else { _objectType = "Left4Flags"; } offset = 0.75F; break; case DurationClass.fiveFlags: if (_stemDirection == VerticalDir.up) { _objectType = "Right5Flags"; } else { _objectType = "Left5Flags"; } offset = 1F; break; default: Debug.Assert(false, "This duration class has no flags."); break; } if (_stemDirection == VerticalDir.up) { _top = 0F; _bottom = (0.2467F + offset) * fontHeight; } else { _top = (-(0.2467F + offset)) * fontHeight; _bottom = 0F; } }
public override void ShiftYsForBeamBlock(float outerLeftY, float gap, VerticalDir stemDirection, float beamThickness) { ShiftYsForBeamBlock(outerLeftY, gap, stemDirection, beamThickness, 1); }
/// <summary> /// Should be called with a duration class having a flag block /// </summary> public FlagsBlockMetrics(DurationClass durationClass, float fontHeight, VerticalDir stemDirection) : base() { _left = 0F; _right = 0.31809F * fontHeight; _originX = 0F; _originY = 0F; _fontHeight = fontHeight; _stemDirection = stemDirection; float offset = 0F; switch(durationClass) { case DurationClass.quaver: if(_stemDirection == VerticalDir.up) _objectType = "Right1Flag"; else _objectType = "Left1Flag"; break; case DurationClass.semiquaver: if(_stemDirection == VerticalDir.up) _objectType = "Right2Flags"; else _objectType = "Left2Flags"; offset = 0.25F; break; case DurationClass.threeFlags: if(_stemDirection == VerticalDir.up) _objectType = "Right3Flags"; else _objectType = "Left3Flags"; offset = 0.5F; break; case DurationClass.fourFlags: if(_stemDirection == VerticalDir.up) _objectType = "Right4Flags"; else _objectType = "Left4Flags"; offset = 0.75F; break; case DurationClass.fiveFlags: if(_stemDirection == VerticalDir.up) _objectType = "Right5Flags"; else _objectType = "Left5Flags"; offset = 1F; break; default: Debug.Assert(false, "This duration class has no flags."); break; } if(_stemDirection == VerticalDir.up) { _top = 0F; _bottom = (0.2467F + offset) * fontHeight; } else { _top = (-(0.2467F + offset)) * fontHeight; _bottom = 0F; } }
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); }
/// <summary> /// Should be called with a duration class having a flag block /// </summary> public FlagsMetrics(DurationClass durationClass, double fontHeight, VerticalDir stemDirection) : base(CSSObjectClass.flag) { _left = 0; // (0.31809F * fontHeight) is maximum x in the flag def. _right = (0.31809F * fontHeight); if (stemDirection == VerticalDir.up) { double rightPadding = (0.06F * fontHeight); _right += rightPadding; } _originX = 0; _originY = 0; double offset = 0; switch (durationClass) { // Bravura says there is a maximum of 8 flags case DurationClass.quaver: if (stemDirection == VerticalDir.up) { _flagID = FlagID.right1Flag; } else { _flagID = FlagID.left1Flag; } break; case DurationClass.semiquaver: if (stemDirection == VerticalDir.up) { _flagID = FlagID.right2Flags; } else { _flagID = FlagID.left2Flags; } offset = 0.25; break; case DurationClass.threeFlags: if (stemDirection == VerticalDir.up) { _flagID = FlagID.right3Flags; } else { _flagID = FlagID.left3Flags; } offset = 0.5; break; case DurationClass.fourFlags: if (stemDirection == VerticalDir.up) { _flagID = FlagID.right4Flags; } else { _flagID = FlagID.left4Flags; } offset = 0.75; break; case DurationClass.fiveFlags: if (stemDirection == VerticalDir.up) { _flagID = FlagID.right5Flags; } else { _flagID = FlagID.left5Flags; } offset = 1; break; case DurationClass.sixFlags: if (stemDirection == VerticalDir.up) { _flagID = FlagID.right6Flags; } else { _flagID = FlagID.left6Flags; } offset = 1.25; break; case DurationClass.sevenFlags: if (stemDirection == VerticalDir.up) { _flagID = FlagID.right7Flags; } else { _flagID = FlagID.left7Flags; } offset = 1.5; break; case DurationClass.eightFlags: if (stemDirection == VerticalDir.up) { _flagID = FlagID.right8Flags; } else { _flagID = FlagID.left8Flags; } offset = 1.75; break; default: M.Assert(false, "This duration class has no flags."); break; } if (stemDirection == VerticalDir.up) { _top = 0; _bottom = (0.2467F + offset) * fontHeight; } else { _top = (-(0.2467F + offset)) * fontHeight; _bottom = 0; } if (!_usedFlagIDs.Contains((FlagID)_flagID)) { _usedFlagIDs.Add((FlagID)_flagID); } }
private void GetRelativePositions(VerticalDir voiceStemDirection, LyricMetrics lyricMetrics, out bool ornamentIsBelow, out bool dynamicIsBelow) { dynamicIsBelow = true; ornamentIsBelow = false; switch(voiceStemDirection) { case VerticalDir.none: // a 1-Voice staff if(lyricMetrics != null && lyricMetrics.IsBelow) dynamicIsBelow = false; break; case VerticalDir.up: // top voice of a 2-Voice staff dynamicIsBelow = false; ornamentIsBelow = false; break; case VerticalDir.down: // bottom voice of a 2-Voice staff dynamicIsBelow = true; ornamentIsBelow = true; break; } }
/// <summary> /// Shifts a horizontal beam vertically to the correct position (wrt the beamBlock) for its duration class /// </summary> /// <param name="outerLeftY"></param> /// <param name="gap"></param> /// <param name="stemDirection"></param> /// <param name="beamThickness"></param> /// <param name="nGaps"></param> protected void ShiftYsForBeamBlock(float outerLeftY, float gap, VerticalDir stemDirection, float beamThickness, int nGaps) { float dy = 0F; if(stemDirection == VerticalDir.down) { dy = -(beamThickness + (gap * nGaps)); } else { dy = gap * nGaps; } dy += outerLeftY - _leftTopY ; MoveYs(dy, dy); }
/// <summary> /// This algorithm follows Gardner Read when the stemDirection is "none" (i.e. not forced): /// If there were no beam, and the majority of the stems would go up, then all the stems in the beam go up. /// ji: if there are the same number of default up and default down stems, then the direction is decided by /// the most extreme notehead in the beam group. If both extremes are the same (e.g. 1 ledgeline up and down) /// then the stems are all down. /// </summary> /// <param name="currentClef"></param> /// <param name="chordsBeamedTogether"></param> private void SetBeamedGroupStemDirection(ClefSymbol currentClef, List<ChordSymbol> chordsBeamedTogether, VerticalDir voiceStemDirection) { Debug.Assert(chordsBeamedTogether.Count > 1); VerticalDir groupStemDirection = voiceStemDirection; if(voiceStemDirection == VerticalDir.none) { // here, there is only one voice in the staff, so the direction depends on the height of the noteheads. int upStems = 0; int downStems = 0; foreach(ChordSymbol chord in chordsBeamedTogether) { VerticalDir direction = chord.DefaultStemDirection(currentClef); if(direction == VerticalDir.up) upStems++; else downStems++; } if(upStems == downStems) groupStemDirection = GetDirectionFromExtremes(currentClef, chordsBeamedTogether); else if(upStems > downStems) groupStemDirection = VerticalDir.up; else groupStemDirection = VerticalDir.down; } foreach(ChordSymbol chord in chordsBeamedTogether) { chord.Stem.Direction = groupStemDirection; } }
/// <summary> /// topBoundary is set to topStaffline /// bottomBoundary is set to bottomStaffline /// then these bounds are widened if the chord lies outside: /// If there is no stem, /// topBoundary is set to topNotehead.Top or top of top accidental /// bottomBoundary is set to bottomNotehead.Bottom (but not to bottom of bottom accidental). /// else if stem is up, /// topBoundardy is set to stemTopTipY, /// bottomBoundary is set to bottomNotehead.Bottom (but not to bottom of bottom accidental). /// else if stem is down, /// topBoundary is set to topNotehead.Top or top of top accidental, /// bottomBoundary is set to stemBottomTipY or bottomNoteheadBottom or bottom of bottom accidental. /// </summary> private void GetTopAndBottomBounds(VerticalDir stemDirection, out float topBoundary, out float bottomBoundary) { float topStaffline = _clef.Voice.Staff.Metrics.StafflinesTop; topBoundary = topStaffline; // top of staff bottomBoundary = topStaffline + (_gap * 4F); // bottom of staff if(_stemMetrics == null) { float topOfTopHead = _headsMetricsTopDown[0].Top; topBoundary = (topBoundary < topOfTopHead) ? topBoundary : topOfTopHead; float bottomOfBottomHead = _headsMetricsTopDown[_headsMetricsTopDown.Count - 1].Bottom; bottomBoundary = (bottomBoundary > bottomOfBottomHead) ? bottomBoundary : bottomOfBottomHead; if(_topDownAccidentalsMetrics != null) { Debug.Assert(_topDownAccidentalsMetrics.Count > 0); float topOfTopAccidental = _topDownAccidentalsMetrics[0].Top; topBoundary = (topBoundary < topOfTopAccidental) ? topBoundary : topOfTopAccidental; //float bottomOfBottomAccidental = _topDownAccidentalsMetrics[_topDownAccidentalsMetrics.Count - 1].Bottom; //bottomBoundary = (bottomBoundary > bottomOfBottomAccidental) ? bottomBoundary : bottomOfBottomAccidental; } } else if(_stemMetrics.VerticalDir == VerticalDir.up) { topBoundary = (topBoundary < _stemMetrics.Top) ? topBoundary : _stemMetrics.Top; float bottomOfBottomHead = _headsMetricsTopDown[_headsMetricsTopDown.Count - 1].Bottom; bottomBoundary = (bottomBoundary > bottomOfBottomHead) ? bottomBoundary : bottomOfBottomHead; //if(_topDownAccidentalsMetrics != null) //{ // Debug.Assert(_topDownAccidentalsMetrics.Count > 0); // float bottomOfBottomAccidental = _topDownAccidentalsMetrics[_topDownAccidentalsMetrics.Count - 1].Bottom; // bottomBoundary = (bottomBoundary > bottomOfBottomAccidental) ? bottomBoundary : bottomOfBottomAccidental; //} } else { float topOfTopHead = _headsMetricsTopDown[0].Top; topBoundary = (topBoundary < topOfTopHead) ? topBoundary : topOfTopHead; if(_topDownAccidentalsMetrics != null) { Debug.Assert(_topDownAccidentalsMetrics.Count > 0); float topOfTopAccidental = _topDownAccidentalsMetrics[0].Top; topBoundary = (topBoundary < topOfTopAccidental) ? topBoundary : topOfTopAccidental; } bottomBoundary = (bottomBoundary > _stemMetrics.Bottom) ? bottomBoundary : _stemMetrics.Bottom; } }
public abstract Metrics NoteObjectMetrics(Graphics graphics, NoteObject noteObject, VerticalDir voiceStemDirection, float gap, float storkeWidth);
/// <summary> /// Called when the ChordMetrics is first constructed. /// It does not yet have any beams. /// </summary> private void MoveAuxilliaries(VerticalDir stemDirection, float gap) { MoveAuxilliaries(stemDirection, gap, 0F, 0F); }
public override void ShiftYsForBeamBlock(float outerLeftY, float gap, VerticalDir stemDirection, float beamThickness) { ShiftYsForBeamBlock(outerLeftY, gap, stemDirection, beamThickness, 2); }
/// <summary> /// The upper beam padding and lower beam padding is used exclusively for auxilliaries placed next to beams. /// These values do not affect the positions of auxilliaries on ordinary flags. /// </summary private void MoveAuxilliaries(VerticalDir stemDirection, float gap, float upperBeamPadding, float lowerBeamPadding) { float topBoundary; float bottomBoundary; GetTopAndBottomBounds(stemDirection, out topBoundary, out bottomBoundary); topBoundary -= upperBeamPadding; bottomBoundary += lowerBeamPadding; // These are moved to a position relative to the outer stem tip or notehead. if(_ornamentMetrics != null) MoveOrnamentMetrics(gap, ref topBoundary, ref bottomBoundary); if(_lyricMetrics != null) MoveLyricMetrics(gap, ref topBoundary, ref bottomBoundary); if(_dynamicMetrics != null) MoveDynamicMetrics(gap, ref topBoundary, ref bottomBoundary); }
public abstract void ShiftYsForBeamBlock(double outerLeftY, double gap, VerticalDir stemDirection, double beamThickness);
/// <summary> /// returns null if there is no lyric in the _drawObjects /// </summary> /// <param name="graphics"></param> /// <param name="gap"></param> /// <param name="lyricIsBelow"></param> /// <returns></returns> private LyricMetrics NewLyricMetrics(VerticalDir voiceStemDirection, Graphics graphics, float gap) { LyricText lyric = null; foreach(DrawObject drawObject in _drawObjects) { lyric = drawObject as LyricText; if(lyric != null) break; } _lyricMetrics = null; if(lyric != null) { bool lyricIsBelow = true; // voiceStemDirection == VerticalDir.none || voiceStemDirection == VerticalDir.down if(voiceStemDirection == VerticalDir.up) lyricIsBelow = false; lyric.Metrics = new LyricMetrics(gap, graphics, lyric.TextInfo, lyricIsBelow); _lyricMetrics = (LyricMetrics)lyric.Metrics; } return _lyricMetrics; }
public override void ShiftYsForBeamBlock(double outerLeftY, double gap, VerticalDir stemDirection, double beamThickness) { ShiftYsForBeamBlock(outerLeftY, gap, stemDirection, beamThickness, 0); }
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; }