public float CalculateBeamY(float stemSize, float xCorrection, float xPosition, float scale, Func<Note, float> yPosition)
{
// create a line between the min and max note of the group
var direction = Direction;
if (Beats.Count == 1)
{
if (direction == BeamDirection.Up)
{
return yPosition(MaxNote) - stemSize;
}
return yPosition(MinNote) + stemSize;
}
// we use the min/max notes to place the beam along their real position
// we only want a maximum of 10 offset for their gradient
var maxDistance = (10 * scale);
// if the min note is not first or last, we can align notes directly to the position
// of the min note
if (direction == BeamDirection.Down && MinNote != FirstMinNote && MinNote != LastMinNote)
{
return yPosition(MinNote) + stemSize;
}
if (direction == BeamDirection.Up && MaxNote != FirstMaxNote && MaxNote != LastMaxNote)
{
return yPosition(MaxNote) - stemSize;
}
float startX = GetBeatLineX(FirstMinNote.Beat) + xCorrection;
float startY = direction == BeamDirection.Up
? yPosition(FirstMaxNote) - stemSize
: yPosition(FirstMinNote) + stemSize;
float endX = GetBeatLineX(LastMaxNote.Beat) + xCorrection;
float endY = direction == BeamDirection.Up
? yPosition(LastMaxNote) - stemSize
: yPosition(LastMinNote) + stemSize;
// ensure the maxDistance
if (direction == BeamDirection.Down && startY > endY && (startY - endY) > maxDistance) endY = (startY - maxDistance);
if (direction == BeamDirection.Down && endY > startY && (endY - startY) > maxDistance) startY = (endY - maxDistance);
if (direction == BeamDirection.Up && startY < endY && (endY - startY) > maxDistance) endY = (startY + maxDistance);
if (direction == BeamDirection.Up && endY < startY && (startY - endY) > maxDistance) startY = (endY + maxDistance);
// get the y position of the given beat on this curve
if (startX == endX) return startY;
// y(x) = ( (y2 - y1) / (x2 - x1) ) * (x - x1) + y1;
return ((endY - startY) / (endX - startX)) * (xPosition - startX) + startY;
}