/// <summary> /// Writes out an SVG Voice /// </summary> /// <param name="w"></param> public virtual void WriteSVG(SvgWriter w, bool staffIsVisible) { for (int i = 0; i < NoteObjects.Count; ++i) { NoteObject noteObject = NoteObjects[i]; Barline barline = noteObject as Barline; if (staffIsVisible && barline != null) { bool isLastNoteObject = (i == (NoteObjects.Count - 1)); float top = Staff.Metrics.StafflinesTop; float bottom = Staff.Metrics.StafflinesBottom; PageFormat pageFormat = Staff.SVGSystem.Score.PageFormat; float barlineStrokeWidth = pageFormat.BarlineStrokeWidth; float stafflineStrokeWidth = pageFormat.StafflineStemStrokeWidth; barline.WriteSVG(w, top, bottom, barlineStrokeWidth, stafflineStrokeWidth, isLastNoteObject, false); } ChordSymbol chordSymbol = noteObject as ChordSymbol; if (chordSymbol != null) { chordSymbol.WriteSVG(w, staffIsVisible); } else { // if this is the first barline, the staff name and (maybe) barnumber will be written. noteObject.WriteSVG(w, staffIsVisible); } } }
/// <summary> /// This function moves the lowerChord to the left or right in order to avoid collisions with /// the noteheads of the upper chord. /// The positions of noteheads and ledgerlines relative to their own stem is never changed. /// The positions of accidentals are adjusted in both chords after the lower chord has moved. /// Stem lengths, and the positions of flags and beamBlocks, are adjusted later in FinalizeBeamBlocks(). /// /// The stems are currently at the standard x-positions for stems up and down at the same MsPosition. /// Their lengths have been changed if necessary, for crossing parts. So collisions can be checked /// reliably in this function. /// There are 7 possible horizontal positions for the stem of the lower chord: /// 1. the standard position (aligned as when the lower chord is well below the upper chord) /// 2. hairline left of upper noteheads /// 3. aligned with upper stem /// (top notehead of the bottom chord is half a space below the bottom notehead of the upper chord) /// 4. thin hairline right of upper stem /// (top notehead of the bottom chord is at the same height as the bottom notehead of the upper chord) /// 5. thick hairline right of upper stem /// (top notehead of the bottom chord is above the bottom notehead of the upper chord) /// If both upper and lower chords have sideways shifted noteheads, and there are no notehead collisions: /// 6. thick hairline right of right-side note head on upper stem /// else /// 7. a head width left of its original position /// The position selected, is the first of these which can be applied without causing any collisions. /// The principle is that the total width of all the noteheads should be minimized. /// Accidentals are rearranged (top to bottom, to the left of the combined chord) once the /// noteheads and ledgerlines have been given their final positions. /// </summary> private void AdjustLowerChordXPosition(ChordSymbol upperChord, ChordSymbol lowerChord) { Debug.Assert(upperChord.MsPosition == lowerChord.MsPosition); if (!(upperChord is CautionaryChordSymbol)) { Debug.Assert(upperChord.Stem.Direction == VerticalDir.up); } if (!(lowerChord is CautionaryChordSymbol)) { Debug.Assert(lowerChord.Stem.Direction == VerticalDir.down); } List <HeadMetrics> upperChordHeadMetrics = upperChord.ChordMetrics.HeadsMetrics; // a clone List <HeadMetrics> lowerChordHeadMetrics = lowerChord.ChordMetrics.HeadsMetrics; // a clone StemMetrics lowerChordStemMetrics = lowerChord.ChordMetrics.StemMetrics; // a clone float deltaX = LowerChordDeltaX(upperChordHeadMetrics, lowerChordHeadMetrics, lowerChordStemMetrics); if (deltaX != 0) { lowerChord.ChordMetrics.Move(deltaX, 0F); // move the whole chord, including accidentals } // adjust the positions of accidentals in both chords lowerChord.AdjustAccidentalsX(upperChord); }
public Stem(ChordSymbol chordSymbol, Stem stem) { Chord = chordSymbol; if(stem != null) { Direction = stem.Direction; } }
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 bool Draw = true; // set to false for cautionary chords #endregion Fields #region Constructors public Stem(ChordSymbol chordSymbol, bool beamContinues) { Chord = chordSymbol; if(chordSymbol.DurationClass == DurationClass.breve || chordSymbol.DurationClass == DurationClass.semibreve || chordSymbol.DurationClass == DurationClass.minim || chordSymbol.DurationClass == DurationClass.crotchet) BeamContinues = false; else BeamContinues = beamContinues; }
public bool Draw = true; // set to false for cautionary chords #endregion Fields #region Constructors public Stem(ChordSymbol chordSymbol) { Chord = chordSymbol; if(chordSymbol.DurationClass == DurationClass.breve || chordSymbol.DurationClass == DurationClass.semibreve || chordSymbol.DurationClass == DurationClass.minim || chordSymbol.DurationClass == DurationClass.crotchet) BeamContinues = false; else BeamContinues = true; // theoretically, I can reset this default value later (when breaking beams programmatically) }
public HeadMetrics(ChordSymbol chord, float gapVBPX) : base(chord.DurationClass, false, chord.FontHeight) { Move((Left - Right) / 2F, 0F); // centre horizontally float horizontalPadding = chord.FontHeight * 0.04F; _leftStemX = _left; _rightStemX = _right; _left -= horizontalPadding; _right += horizontalPadding; }
private void AddExtendersAtTheEndsOfStaves(List <Staff> staves, float rightMarginPos, float gap, float extenderStrokeWidth, float hairlinePadding, SvgSystem nextSystem) { for (int staffIndex = 0; staffIndex < staves.Count; ++staffIndex) { Staff staff = staves[staffIndex]; if (!(staff is InvisibleOutputStaff)) { for (int voiceIndex = 0; voiceIndex < staff.Voices.Count; ++voiceIndex) { Voice voice = staff.Voices[voiceIndex]; List <NoteObject> noteObjects = voice.NoteObjects; ChordSymbol lastChord = null; RestSymbol lastRest = null; CautionaryChordSymbol cautionary = null; for (int index = noteObjects.Count - 1; index >= 0; --index) { lastChord = noteObjects[index] as ChordSymbol; lastRest = noteObjects[index] as RestSymbol; cautionary = noteObjects[index] as CautionaryChordSymbol; if (cautionary != null) { cautionary.Visible = false; // a CautionaryChordSymbol is a ChordSymbol, but we have not found a real one yet. } else if (lastChord != null || lastRest != null) { break; } } if (lastChord != null && lastChord.MsDurationToNextBarline != null) { List <float> x1s = GetX1sFromChord1(lastChord.ChordMetrics, hairlinePadding); List <float> x2s; List <float> ys = lastChord.ChordMetrics.HeadsOriginYs; if (nextSystem != null && FirstDurationSymbolOnNextSystemIsCautionary(nextSystem.Staves[staffIndex].Voices[voiceIndex])) { x2s = GetEqualFloats(rightMarginPos + gap, x1s.Count); } else { x2s = GetEqualFloats(rightMarginPos, x1s.Count); } lastChord.ChordMetrics.NoteheadExtendersMetrics = CreateExtenders(x1s, x2s, ys, extenderStrokeWidth, gap, true); } } } } }
public Stem(ChordSymbol chordSymbol) { Chord = chordSymbol; if (chordSymbol.DurationClass == DurationClass.breve || chordSymbol.DurationClass == DurationClass.semibreve || chordSymbol.DurationClass == DurationClass.minim || chordSymbol.DurationClass == DurationClass.crotchet) { BeamContinues = false; } else { BeamContinues = true; // theoretically, I can reset this default value later (when breaking beams programmatically) } }
public HeadMetrics(ChordSymbol chord, Head head, double gapVBPX, CSSObjectClass headClass) : base(chord.DurationClass, chord.FontHeight, headClass) { Move((Left - Right) / 2, 0F); // centre horizontally double horizontalPadding = chord.FontHeight * 0.04; _leftStemX = _left; _rightStemX = _right; _left -= horizontalPadding; _right += horizontalPadding; if (head != null) { CSSColorClass = head.ColorClass; } }
private float BeamEndMsPosition(Beam beam) { Debug.Assert(this.Beams.Contains(beam)); float beamEndMsPosition = float.MinValue; for (int i = Chords.Count - 1; i >= 0; --i) { ChordSymbol chord = Chords[i]; float stemX = chord.ChordMetrics.StemMetrics.OriginX; if (stemX == beam.LeftX || stemX == beam.RightX) // rightX can be a beam stub { beamEndMsPosition = chord.MsPosition; break; } } Debug.Assert(beamEndMsPosition != float.MinValue); return(beamEndMsPosition); }
/// <summary> /// Construct a new Head with a particular midi pitch number. /// </summary> /// <param name="chord">The containing ChordSymbol</param> /// <param name="midiPitch">The midiPitch. Must be in range [0..127]</param> /// <param name="midiVelocity">The midiVelocity. Must be in range [0..127] for output Heads.</param> /// <param name="useSharp">true means use #, false means use flat(if there is a choice).</param> public Head(ChordSymbol chord, int midiPitch, int midiVelocity, bool useSharp) { Chord = chord; KeyValuePair <string, int> sharpKVP = new KeyValuePair <string, int>(); KeyValuePair <string, int> flatKVP = new KeyValuePair <string, int>(); KeyValuePair <string, int> previousKVP = new KeyValuePair <string, int>("C0", 0); foreach (KeyValuePair <string, int> kvp in M.MidiPitchDict) { if (kvp.Value >= midiPitch) { flatKVP = kvp; sharpKVP = previousKVP; break; } previousKVP = kvp; } if (flatKVP.Value == midiPitch) { Pitch = flatKVP.Key; Alteration = 0; } else if (useSharp) { Pitch = sharpKVP.Key; Alteration = midiPitch - sharpKVP.Value; } else { Pitch = flatKVP.Key; Alteration = midiPitch - flatKVP.Value; } if (Alteration != 0) { DisplayAccidental = DisplayAccidental.force; } if (chord != null) { FontSize = chord.FontHeight; } MidiVelocity = midiVelocity; }
/// <summary> /// Construct a new Head with a particular midi pitch number. /// </summary> /// <param name="chord">The containing ChordSymbol</param> /// <param name="midiPitch">The midiPitch. Must be in range [0..127]</param> /// <param name="midiVelocity">The midiVelocity. Must be in range [0..127] for output Heads. Is -1 for input Heads</param> /// <param name="useSharp">true means use #, false means use flat(if there is a choice).</param> public Head(ChordSymbol chord, int midiPitch, int midiVelocity, bool useSharp) { Chord = chord; KeyValuePair<string, int> sharpKVP = new KeyValuePair<string, int>(); KeyValuePair<string, int> flatKVP = new KeyValuePair<string, int>(); KeyValuePair<string, int> previousKVP = new KeyValuePair<string, int>("C0", 0); foreach(KeyValuePair<string, int> kvp in M.MidiPitchDict) { if(kvp.Value >= midiPitch) { flatKVP = kvp; sharpKVP = previousKVP; break; } previousKVP = kvp; } if(flatKVP.Value == midiPitch) { Pitch = flatKVP.Key; Alteration = 0; } else if(useSharp) { Pitch = sharpKVP.Key; Alteration = midiPitch - sharpKVP.Value; } else { Pitch = flatKVP.Key; Alteration = midiPitch - flatKVP.Value; } if(Alteration != 0) DisplayAccidental = DisplayAccidental.force; if(chord != null) FontSize = chord.FontHeight; _midiVelocity = midiVelocity; }
/// <summary> /// Force the display of naturals where the synchronous chords share a diatonic pitch, /// and one of them is not natural. /// </summary> private void ForceNaturals(ChordSymbol synchChord1, ChordSymbol synchChord2) { Debug.Assert(synchChord1.MsPosition == synchChord2.MsPosition); foreach (Head head1 in synchChord1.HeadsTopDown) { foreach (Head head2 in synchChord2.HeadsTopDown) { if (head1.Pitch == head2.Pitch) { if (head1.Alteration != 0) { head2.DisplayAccidental = DisplayAccidental.force; } if (head2.Alteration != 0) { head1.DisplayAccidental = DisplayAccidental.force; } break; } } } }
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); }
private List <float> GetCautionaryRightExtenderX2s(CautionaryChordSymbol cautionaryChordSymbol1, List <NoteObject> noteObjects, List <float> x1s, List <float> ys, float hairlinePadding) { List <float> x2s = new List <float>(); NoteObject no2 = GetFollowingChordRestOrBarlineSymbol(noteObjects); Barline barline = no2 as Barline; ChordSymbol chord2 = no2 as ChordSymbol; RestSymbol rest2 = no2 as RestSymbol; if (barline != null) { float x2 = barline.Metrics.OriginX; x2s = GetEqualFloats(x2, x1s.Count); } else if (chord2 != null) { x2s = GetX2sFromChord2(ys, chord2.ChordMetrics, hairlinePadding); } else if (rest2 != null) { float x2 = rest2.Metrics.Left - hairlinePadding; x2s = GetEqualFloats(x2, x1s.Count); } else // no2 == null { Debug.Assert(no2 == null); // This voice has no further chords or rests, // so draw extenders to the right margin. // extend to the right margin PageFormat pageFormat = cautionaryChordSymbol1.Voice.Staff.SVGSystem.Score.PageFormat; float rightMarginPos = pageFormat.RightMarginPos; float gap = pageFormat.Gap; x2s = GetEqualFloats(rightMarginPos + gap, ys.Count); } return(x2s); }
private HashSet <ChordSymbol> FindChordSymbolsThatStartBeamBlocks() { HashSet <ChordSymbol> chordSymbolsThatStartBeamBlocks = new HashSet <ChordSymbol>(); var chordSymbols = new List <ChordSymbol>(); foreach (ChordSymbol symb in ChordSymbols) { chordSymbols.Add(symb); } for (int i = 0; i < chordSymbols.Count; i++) { ChordSymbol chord = chordSymbols[i]; if (chord.BeamBlockDef != null) { if ((chord.IsBeamStart) || (i == 0 && (chord.IsBeamRestart || chord.IsBeamEnd))) { chordSymbolsThatStartBeamBlocks.Add(chord); } } } return(chordSymbolsThatStartBeamBlocks); }
private void AddExtendersAtTheBeginningsofStaves(List <Staff> staves, float rightMarginPos, float gap, float extenderStrokeWidth, float hairlinePadding) { foreach (Staff staff in staves) { if (!(staff is InvisibleOutputStaff)) { foreach (Voice voice in staff.Voices) { List <NoteObject> noteObjects = voice.NoteObjects; ClefSymbol firstClef = null; CautionaryChordSymbol cautionaryChordSymbol = null; ChordSymbol firstChord = null; RestSymbol firstRest = null; for (int index = 0; index < noteObjects.Count; ++index) { if (firstClef == null) { firstClef = noteObjects[index] as ClefSymbol; } if (cautionaryChordSymbol == null) { cautionaryChordSymbol = noteObjects[index] as CautionaryChordSymbol; } if (firstChord == null) { firstChord = noteObjects[index] as ChordSymbol; } if (firstRest == null) { firstRest = noteObjects[index] as RestSymbol; } if (firstClef != null && (cautionaryChordSymbol != null || firstChord != null || firstRest != null)) { break; } } if (firstClef != null && cautionaryChordSymbol != null) { // create brackets List <CautionaryBracketMetrics> cbMetrics = cautionaryChordSymbol.ChordMetrics.CautionaryBracketsMetrics; Debug.Assert(cbMetrics.Count == 2); Metrics clefMetrics = firstClef.Metrics; // extender left of cautionary List <float> ys = cautionaryChordSymbol.ChordMetrics.HeadsOriginYs; List <float> x1s = GetEqualFloats(clefMetrics.Right - (hairlinePadding * 2), ys.Count); List <float> x2s = GetEqualFloats(cbMetrics[0].Left, ys.Count); for (int i = 0; i < x2s.Count; ++i) { if ((x2s[i] - x1s[i]) < gap) { x1s[i] = x2s[i] - gap; } } cautionaryChordSymbol.ChordMetrics.NoteheadExtendersMetricsBefore = CreateExtenders(x1s, x2s, ys, extenderStrokeWidth, gap, true); // extender right of cautionary x1s = GetEqualFloats(cbMetrics[1].Right, ys.Count); x2s = GetCautionaryRightExtenderX2s(cautionaryChordSymbol, voice.NoteObjects, x1s, ys, hairlinePadding); cautionaryChordSymbol.ChordMetrics.NoteheadExtendersMetrics = CreateExtenders(x1s, x2s, ys, extenderStrokeWidth, gap, true); } } } } }
/// <summary> /// Sets Chord.Stem.Direction for each chord. /// Chords are beamed together, duration classes permitting, unless a rest or clef intervenes. /// If a barline intervenes, and beamsCrossBarlines is true, the chords are beamed together. /// If a barline intervenes, and beamsCrossBarlines is false, the beam is broken. /// </summary> public void SetChordStemDirectionsAndCreateBeamBlocks(PageFormat pageFormat) { List <ChordSymbol> chordsBeamedTogether = new List <ChordSymbol>(); ClefSymbol currentClef = null; bool breakGroup = false; ChordSymbol lastChord = null; foreach (ChordSymbol cs in ChordSymbols) { lastChord = cs; } foreach (NoteObject noteObject in NoteObjects) { CautionaryChordSymbol cautionaryChord = noteObject as CautionaryChordSymbol; ChordSymbol chord = noteObject as ChordSymbol; RestSymbol rest = noteObject as RestSymbol; ClefSymbol clef = noteObject as ClefSymbol; Barline barline = noteObject as Barline; if (cautionaryChord != null) { continue; } if (chord != null) { if (chord.DurationClass == DurationClass.cautionary || chord.DurationClass == DurationClass.breve || chord.DurationClass == DurationClass.semibreve || chord.DurationClass == DurationClass.minim || chord.DurationClass == DurationClass.crotchet) { if (currentClef != null) { if (this.StemDirection == VerticalDir.none) { chord.Stem.Direction = chord.DefaultStemDirection(currentClef); } else { chord.Stem.Direction = this.StemDirection; } } breakGroup = true; } else { chordsBeamedTogether.Add(chord); if (chord.Stem.BeamContinues) // this is true by default { breakGroup = false; } else { breakGroup = true; } } } if (chordsBeamedTogether.Count > 0) { if (rest != null) { if (rest.LocalCautionaryChordDef == null) { breakGroup = true; } } if (clef != null) { breakGroup = true; } if (barline != null && !pageFormat.BeamsCrossBarlines) { breakGroup = true; } if (chord == lastChord) { breakGroup = true; } } if (chordsBeamedTogether.Count > 0 && breakGroup) { if (currentClef != null) { if (chordsBeamedTogether.Count == 1) { if (this.StemDirection == VerticalDir.none) { chordsBeamedTogether[0].Stem.Direction = chordsBeamedTogether[0].DefaultStemDirection(currentClef); } else { chordsBeamedTogether[0].Stem.Direction = this.StemDirection; } } else if (chordsBeamedTogether.Count > 1) { float beamThickness = pageFormat.BeamThickness; float beamStrokeThickness = pageFormat.StafflineStemStrokeWidth; if (this is InputVoice) { beamThickness *= pageFormat.InputStavesSizeFactor; beamStrokeThickness *= pageFormat.InputStavesSizeFactor; } chordsBeamedTogether[0].BeamBlock = new BeamBlock(currentClef, chordsBeamedTogether, this.StemDirection, beamThickness, beamStrokeThickness); } } chordsBeamedTogether.Clear(); } if (clef != null) { currentClef = clef; } } }
/// <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); }
public Stem(ChordSymbol chordSymbol) { Chord = chordSymbol; }
/// <summary> /// This chordSymbol is in the lower of two voices on a staff. The argument is another synchronous chordSymbol /// at the same MsPosition on the same staff. Both chordSymbols have ChordMetrics, and the chord in the lower /// voice has been moved (either right or left) so that there are no collisions between noteheads. /// This function moves the accidentals in both chords horizontally, so that they are all on the left of both /// chords but as far to the right as possible without there being any collisions. /// Accidentals are positioned in top-bottom and right-left order. /// If two noteheads are at the same diatonic height, both accidentals will already exist and have forced display. /// Such accidentals are placed in the left-right order of the noteheads /// </summary> /// <param name="upperChord"></param> public void AdjustAccidentalsX(ChordSymbol upperChord) { float stafflineStemStrokeWidth = Voice.Staff.SVGSystem.Score.PageFormat.StafflineStemStrokeWidth; this.ChordMetrics.AdjustAccidentalsForTwoChords(upperChord.ChordMetrics, stafflineStemStrokeWidth); }
public HeadMetrics(ChordSymbol chord, Head head, float gapVBPX) : base(chord.DurationClass, false, chord.FontHeight) { Move((Left - Right) / 2F, 0F); // centre horizontally float horizontalPadding = chord.FontHeight * 0.04F; _leftStemX = _left; _rightStemX = _right; _left -= horizontalPadding; _right += horizontalPadding; if(head != null && head.ColorAttribute != null) { _colorAttribute = head.ColorAttribute; } }
private void AdjustStemLengths(ChordSymbol upperChord, ChordSymbol lowerChord) { upperChord.ChordMetrics.AdjustStemLengthAndFlagBlock(upperChord.DurationClass, upperChord.FontHeight, lowerChord.ChordMetrics.HeadsMetrics); lowerChord.ChordMetrics.AdjustStemLengthAndFlagBlock(lowerChord.DurationClass, lowerChord.FontHeight, upperChord.ChordMetrics.HeadsMetrics); }
private void AddExtendersInStaves(List <Staff> staves, float extenderStrokeWidth, float gap, float hairlinePadding) { foreach (Staff staff in staves) { if (!(staff is InvisibleOutputStaff)) { foreach (Voice voice in staff.Voices) { List <NoteObject> noteObjects = voice.NoteObjects; int index = 0; while (index < noteObjects.Count - 1) { // noteObjects.Count - 1 because index is immediately incremented when a continuing // chord or rest is found, and it should always be less than noteObjects.Count. ChordSymbol chord1 = noteObjects[index] as ChordSymbol; if (chord1 != null) { List <float> x1s = GetX1sFromChord1(chord1.ChordMetrics, hairlinePadding); List <float> x2s = null; List <float> ys = null; ++index; if (chord1.MsDurationToNextBarline != null) { while (index < noteObjects.Count) { CautionaryChordSymbol cautionaryChordSymbol = noteObjects[index] as CautionaryChordSymbol; ChordSymbol chord2 = noteObjects[index] as ChordSymbol; RestSymbol rest2 = noteObjects[index] as RestSymbol; if (cautionaryChordSymbol != null) { cautionaryChordSymbol.Visible = false; } else if (chord2 != null) { ys = chord1.ChordMetrics.HeadsOriginYs; x2s = GetX2sFromChord2(ys, chord2.ChordMetrics, hairlinePadding); break; } else if (rest2 != null) { float x2 = rest2.Metrics.Left - hairlinePadding; ys = chord1.ChordMetrics.HeadsOriginYs; x2s = GetEqualFloats(x2, x1s.Count); break; } ++index; } if (x2s != null && ys != null) { bool hasContinuingBeamBlock = ((chord1.BeamBlock != null) && (chord1.BeamBlock.Chords[chord1.BeamBlock.Chords.Count - 1] != chord1)); if (hasContinuingBeamBlock) { Debug.Assert(true); } Barline barline = noteObjects[index - 1] as Barline; if (barline != null) { float x2 = barline.Metrics.OriginX; x2s = GetEqualFloats(x2, x1s.Count); } bool drawExtender = false; if (chord1.DurationClass > DurationClass.semiquaver) { drawExtender = true; } if (chord1.DurationClass < DurationClass.crotchet && hasContinuingBeamBlock) { drawExtender = false; } chord1.ChordMetrics.NoteheadExtendersMetrics = CreateExtenders(x1s, x2s, ys, extenderStrokeWidth, gap, drawExtender); } } } else { ++index; } } } } } }
/// <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); }
private void SetStemAndFlags(ChordSymbol chord, List<HeadMetrics> topDownHeadsMetrics, float stemThickness) { DurationClass durationClass = chord.DurationClass; _flagsBlockMetrics = null; if(chord.BeamBlock == null && (durationClass == DurationClass.quaver || durationClass == DurationClass.semiquaver || durationClass == DurationClass.threeFlags || durationClass == DurationClass.fourFlags || durationClass == DurationClass.fiveFlags)) { _flagsBlockMetrics = GetFlagsBlockMetrics(topDownHeadsMetrics, durationClass, chord.FontHeight, chord.Stem.Direction, stemThickness); } if(durationClass == DurationClass.minim || durationClass == DurationClass.crotchet || durationClass == DurationClass.quaver || durationClass == DurationClass.semiquaver || durationClass == DurationClass.threeFlags || durationClass == DurationClass.fourFlags || durationClass == DurationClass.fiveFlags) { _stemMetrics = NewStemMetrics(topDownHeadsMetrics, chord, _flagsBlockMetrics, stemThickness); } }
/// <summary> /// This chordSymbol is in the lower of two voices on a staff. The argument is another synchronous chordSymbol /// at the same MsPosition on the same staff. Both chordSymbols have ChordMetrics, and the chord in the lower /// voice has been moved (either right or left) so that there are no collisions between noteheads. /// This function moves the accidentals in both chords horizontally, so that they are all on the left of both /// chords but as far to the right as possible without there being any collisions. /// Accidentals are positioned in top-bottom and right-left order. /// If two noteheads are at the same diatonic height, both accidentals will already exist and have forced display. /// Such accidentals are placed in the left-right order of the noteheads /// </summary> /// <param name="upperChord"></param> public void AdjustAccidentalsX(ChordSymbol upperChord) { this.ChordMetrics.AdjustAccidentalsForTwoChords(upperChord.ChordMetrics, M.PageFormat.StafflineStemStrokeWidthVBPX); }
/// <summary> /// Force the display of naturals where the synchronous chords share a diatonic pitch, /// and one of them is not natural. /// </summary> private void ForceNaturals(ChordSymbol synchChord1, ChordSymbol synchChord2) { Debug.Assert(synchChord1.AbsMsPosition == synchChord2.AbsMsPosition); foreach(Head head1 in synchChord1.HeadsTopDown) { foreach(Head head2 in synchChord2.HeadsTopDown) { if(head1.Pitch == head2.Pitch) { if(head1.Alteration != 0) head2.DisplayAccidental = DisplayAccidental.force; if(head2.Alteration != 0) head1.DisplayAccidental = DisplayAccidental.force; break; } } } }
private List<float> GetCautionaryRightExtenderX2s(ChordSymbol cautionaryChordSymbol1, List<NoteObject> noteObjects, List<float> x1s, List<float> ys, float hairlinePadding) { List<float> x2s = new List<float>(); NoteObject no2 = GetFollowingChordRestOrBarlineSymbol(noteObjects); Barline barline = no2 as Barline; ChordSymbol chord2 = no2 as ChordSymbol; RestSymbol rest2 = no2 as RestSymbol; if(barline != null) { float x2 = barline.Metrics.OriginX; x2s = GetEqualFloats(x2, x1s.Count); } else if(chord2 != null) { x2s = GetX2sFromChord2(ys, chord2.ChordMetrics, hairlinePadding); } else if(rest2 != null) { float x2 = rest2.Metrics.Left - hairlinePadding; x2s = GetEqualFloats(x2, x1s.Count); } else // no2 == null { Debug.Assert(no2 == null); // This voice has no further chords or rests, // so draw extenders to the right margin. // extend to the right margin PageFormat pageFormat = cautionaryChordSymbol1.Voice.Staff.SVGSystem.Score.PageFormat; float rightMarginPos = pageFormat.RightMarginPos; float gap = pageFormat.Gap; x2s = GetEqualFloats(rightMarginPos + gap, ys.Count); } return x2s; }
private void CreateCautionaryBracketsMetrics(ChordSymbol chord) { PageFormat pageFormat = chord.Voice.Staff.SVGSystem.Score.PageFormat; float gap = pageFormat.Gap; float padding = pageFormat.StafflineStemStrokeWidth; float strokeWidth = pageFormat.StafflineStemStrokeWidth; float top, left, bottom, right; GetAccidentalsAndHeadsBox(out top, out right, out bottom, out left, gap, padding); float leftBracketLeft = left - (gap / 2F); float rightBracketRight = right + (gap / 2F); // the left bracket CautionaryBracketMetrics leftBracketMetrics = new CautionaryBracketMetrics(true, top, left, bottom, leftBracketLeft, strokeWidth); // the right bracket CautionaryBracketMetrics rightBracketMetrics = new CautionaryBracketMetrics(false, top, rightBracketRight, bottom, right, strokeWidth); _cautionaryBracketsMetrics = new List<CautionaryBracketMetrics>(); this._cautionaryBracketsMetrics.Add(leftBracketMetrics); this._cautionaryBracketsMetrics.Add(rightBracketMetrics); }