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. } }
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); }
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(); }
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); }
/// <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); } }
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. }
/// <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); }
/// <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); }
/// <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. }
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" }
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); }
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); }
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(); }
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); }
public override void AddMetricsToEdge(HorizontalEdge horizontalEdge) { ChordMetrics.AddToEdge(horizontalEdge); }
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 } } } } } } } }