/// <summary> /// Calculates slur height trying to avoid collisions with stems /// </summary> /// <param name="note"></param> /// <param name="endPoint"></param> /// <returns></returns> private double DetermineSlurHeight(Note note, Slur slur, SlurInfo slurStartInfo, Point endPoint) { var notesUnderSlur = note.Staff.EnumerateUntilConditionMet <Note>(note, n => n.Slurs.FirstOrDefault(s => s.Number == slur.Number)?.Type == NoteSlurType.Start, true).ToArray(); if (notesUnderSlur.Length < 3) { return(10); } var notesUnderSlurExclusive = notesUnderSlur.Take(notesUnderSlur.Length - 1).Skip(1); var mostExtremePoint = GetMostExtremePoint(notesUnderSlurExclusive, slurStartInfo.StartPlacement); var angle = UsefulMath.BeamAngle(slurStartInfo.StartPoint.X, slurStartInfo.StartPoint.Y, endPoint.X, endPoint.Y); var slurYPositionInMostExtremePoint = slurStartInfo.StartPoint.TranslateHorizontallyAndMaintainAngle(angle, mostExtremePoint.X - slurStartInfo.StartPoint.X).Y; var mostExtremeYPosition = mostExtremePoint.Y; if (slurStartInfo.StartPlacement == VerticalPlacement.Above && mostExtremeYPosition < slurYPositionInMostExtremePoint) { return(Math.Abs(mostExtremeYPosition - slurYPositionInMostExtremePoint) + 10); } if (slurStartInfo.StartPlacement == VerticalPlacement.Below && mostExtremeYPosition > slurYPositionInMostExtremePoint) { return(Math.Abs(slurYPositionInMostExtremePoint - mostExtremeYPosition) + 10); } return(10); }
/// <summary> /// Draws of performs additional logic at slur end /// </summary> /// <param name="renderer">Score renderer</param> /// <param name="slur">Slur</param> /// <param name="element">Element with a slur</param> /// <param name="notePositionY">Y position of element with a slur</param> /// <param name="slurStartInfo">Information about slur start point</param> /// <param name="slurPlacement">Information about slur placement</param> protected abstract void ProcessSlurEnd(ScoreRendererBase renderer, Slur slur, Note element, double notePositionY, SlurInfo slurStartInfo, VerticalPlacement slurPlacement);
/// <summary> /// Draws or performs additional logic at slur start /// </summary> /// <param name="renderer">Score renderer</param> /// <param name="slur">Slur</param> /// <param name="element">Element with a slur</param> /// <param name="notePositionY"></param> /// <param name="slurStartInfo">Information about slur start point</param> /// <param name="slurPlacement">Information about slur placement</param> protected override void ProcessSlurStart(ScoreRendererBase renderer, Slur slur, Note element, double notePositionY, SlurInfo slurStartInfo, VerticalPlacement slurPlacement) { slurStartInfo.StartPoint = RelativeToAbsolute(renderer, renderer.TenthsToPixels(slur.BezierControlPoint), notePositionY); slurStartInfo.BezierStartControlPoint = RelativeToAbsolute(renderer, renderer.TenthsToPixels(slur.BezierStartOrEndPoint), notePositionY); }
/// <summary> /// Draws of performs additional logic at slur end /// </summary> /// <param name="renderer">Score renderer</param> /// <param name="slur">Slur</param> /// <param name="element">Element with a slur</param> /// <param name="notePositionY">Y position of element with a slur</param> /// <param name="slurStartInfo">Information about slur start point</param> /// <param name="slurPlacement">Information about slur placement</param> protected override void ProcessSlurEnd(ScoreRendererBase renderer, Slur slur, Note element, double notePositionY, SlurInfo slurStartInfo, VerticalPlacement slurPlacement) { Point endPoint; var noteheadWidth = element.GetNoteheadWidthPx(renderer); if (slurStartInfo.StartPlacement == VerticalPlacement.Above) { var xShiftConcerningStemDirectionEnd = noteheadWidth / 2; bool hasFlagOrBeam = element.BaseDuration.Denominator > 4; endPoint = new Point(scoreService.CursorPositionX + xShiftConcerningStemDirectionEnd, (element.StemDirection == VerticalDirection.Up ? element.StemEndLocation.Y + (hasFlagOrBeam ? -2 : 0) : notePositionY - 7)); } else if (slurStartInfo.StartPlacement == VerticalPlacement.Below) { var xShiftConcerningStemDirectionEnd = noteheadWidth / 2; endPoint = new Point(scoreService.CursorPositionX + xShiftConcerningStemDirectionEnd, notePositionY + 5); } else { throw new Exception("Unsupported placement type."); } var slurHeight = DetermineSlurHeight(element, slur, slurStartInfo, endPoint); for (int i = 0; i < 3; i++) //Draw a few curves one by one to simulate a curve with variable thickness. It will be replaced by a path in future releases. { var controlPoints = GetBezierControlPoints(slurStartInfo.StartPoint, endPoint, slurStartInfo.StartPlacement, slurHeight + i); renderer.DrawBezier(slurStartInfo.StartPoint, controlPoints.Item1, controlPoints.Item2, endPoint, element); } //DrawSlurFrame(renderer, startPoint, controlPoints.Item1, controlPoints.Item2, endPoint, element); }
/// <summary> /// Draws or performs additional logic at slur start /// </summary> /// <param name="renderer">Score renderer</param> /// <param name="slur">Slur</param> /// <param name="element">Element with a slur</param> /// <param name="notePositionY"></param> /// <param name="slurStartInfo">Information about slur start point</param> /// <param name="slurPlacement">Information about slur placement</param> protected override void ProcessSlurStart(ScoreRendererBase renderer, Slur slur, Note element, double notePositionY, SlurInfo slurStartInfo, VerticalPlacement slurPlacement) { slurStartInfo.StartPlacement = slurPlacement; slurStartInfo.StartPointStemDirection = element.StemDirection; var polihymniaFix = renderer.LinespacesToPixels(element.GetNoteheadWidthLs(renderer) * 0.5); if (slurPlacement == VerticalPlacement.Above) { bool hasFlagOrBeam = element.BaseDuration.Denominator > 4; //If note has a flag or beam start the slur above the note. If not, start a bit to the right and down. var xShiftConcerningStemDirectionStart = slurStartInfo.StartPointStemDirection == VerticalDirection.Up ? (hasFlagOrBeam ? 5 : 10) : 1; slurStartInfo.StartPoint = new Point(scoreService.CursorPositionX + xShiftConcerningStemDirectionStart + polihymniaFix, element.StemDirection == VerticalDirection.Down ? notePositionY - 7 : element.StemEndLocation.Y + (hasFlagOrBeam ? -3 : 8)); } else { slurStartInfo.StartPoint = new Point(scoreService.CursorPositionX + 3 + polihymniaFix, notePositionY + 5); } }