private void RenderBars(Graphics context, Score score, ScoreEditorConfig config, ScoreEditorLook look, float barStartY) { var bars = score.Bars; var primaryBeatMode = look.PrimaryBeatMode; var gridArea = ScoreEditorLayout.GetGridArea(config, context.Bounds); var barArea = ScoreEditorLayout.GetBarArea(config, context.Bounds); var infoArea = ScoreEditorLayout.GetInfoArea(config, context.Bounds); var unit = look.BarLineSpaceUnit; var noteRadius = config.NoteRadius; foreach (var bar in bars) { var numberOfGrids = bar.GetNumberOfGrids(); var visible = ScoreEditorLayout.IsBarVisible(barArea, barStartY, numberOfGrids, unit); if (visible) { if (look.BarInfoTextVisible) { DrawBarInfoText(context, bar, infoArea, barStartY); } DrawBarGrid(context, gridArea, config, barStartY, numberOfGrids, unit, noteRadius, primaryBeatMode); DrawBarOutline(context, bar, barArea, barStartY, numberOfGrids, unit); } barStartY -= numberOfGrids * unit; } }
private void RenderNotes(Graphics context, Score score, ScoreEditorConfig config, ScoreEditorLook look, float scrollOffsetY) { var gridArea = ScoreEditorLayout.GetGridArea(config, context.Bounds); var radius = config.NoteRadius; var specialNoteArea = ScoreEditorLayout.GetSpecialNoteArea(config, context.Bounds); DrawNoteConnections(context, score, gridArea, config, look, scrollOffsetY, scrollOffsetY, radius); DrawNotes(context, score, gridArea, config, look, specialNoteArea, scrollOffsetY, radius); }
// For ScoreEditor public void Render(Graphics context, Score score, ScoreEditorConfig config, ScoreEditorLook look, float scrollOffsetY, Rectangle selectionRectangle) { var hasAnyBar = score?.HasAnyBar ?? false; if (hasAnyBar) { RenderBars(context, score, config, look, scrollOffsetY); } var hasAnyNote = score?.HasAnyNote ?? false; if (hasAnyNote) { RenderNotes(context, score, config, look, scrollOffsetY); } RenderSelectionRectangle(context, selectionRectangle); }
// For ScorePreviewer public void Render(Graphics context, Score score, ScoreEditorConfig config, ScoreEditorLook look, float scrollOffsetY, TimeSpan now) { var hasAnyBar = score?.HasAnyBar ?? false; if (hasAnyBar) { RenderBars(context, score, config, look, scrollOffsetY); } var hasAnyNote = score?.HasAnyNote ?? false; if (hasAnyNote) { RenderNotes(context, score, config, look, scrollOffsetY); } if (look.TimeInfoVisible) { var controlArea = new Rectangle(Point.Zero, context.Bounds.Size); DrawBarTimePreviewInfo(context, controlArea, now); } }
private void DrawBarGrid(Graphics context, Rectangle gridArea, ScoreEditorConfig config, float barStartY, int numberOfGrids, float unit, float noteRadius, PrimaryBeatMode primaryBeatMode) { // Vertical var verticalY1 = barStartY; var verticalY2 = barStartY - numberOfGrids * unit; var verticalPen = _barNormalGridPen; var numColumns = config.NumberOfColumns; for (var i = 0; i < numColumns; ++i) { var x = gridArea.Left + gridArea.Width * i / (numColumns - 1); context.DrawLine(verticalPen, x, verticalY1, x, verticalY2); } // Calculate zooming compensation. var firstClearDrawnRatio = ScoreEditorLayout.BarZoomRatio.FirstOrDefault(i => unit * i >= noteRadius * ScoreEditorLayout.SpaceUnitRadiusRatio); if (firstClearDrawnRatio == 0) { firstClearDrawnRatio = numberOfGrids; } // Calculate primary beat grids. int numBeats; switch (primaryBeatMode) { case PrimaryBeatMode.EveryFourBeats: numBeats = 4; break; case PrimaryBeatMode.EveryThreeBeats: numBeats = 3; break; default: throw new ArgumentOutOfRangeException(nameof(primaryBeatMode), primaryBeatMode, null); } var primaryBeatIndex = numberOfGrids / numBeats; var secondaryBeatIndex = primaryBeatIndex / 2; var textBrush = _gridNumberBrush; var textFont = _gridNumberFont; int visibleGridsPerBeat; // For the usage, see below. if (primaryBeatIndex % firstClearDrawnRatio == 0) { visibleGridsPerBeat = primaryBeatIndex / firstClearDrawnRatio; } else { // This patch does not actually 'patch' very well. But it is still better than splitting // the non-separatable 3-beat measures into some weird 5-beat(=32/6) rendering. visibleGridsPerBeat = primaryBeatIndex * numBeats / 4 / firstClearDrawnRatio; } if (visibleGridsPerBeat <= 0) { visibleGridsPerBeat = 1; } var visibleGridCounter = 1; // Horizontal for (var i = 0; i <= numberOfGrids; ++i) { if (i % firstClearDrawnRatio != 0) { continue; } var currentY = barStartY - unit * i; Pen pen; if (i == 0) { // Grid start. pen = _barGridStartBeatPen; } else if (i % primaryBeatIndex == 0) { // Primary beat. pen = _barPrimaryBeatPen; } else if (i % secondaryBeatIndex == 0) { // Secondary beat. pen = _barSecondaryBeatPen; } else { // Normal grid. pen = _barNormalGridPen; } context.DrawLine(pen, gridArea.Left, currentY, gridArea.Right, currentY); if (i < numberOfGrids) { // Prints like // 1/4 2/4 3/4 4/4 1/4 2/4 ... var text = $"{visibleGridCounter}/{visibleGridsPerBeat}"; var textSize = context.MeasureString(_gridNumberFont, text); var textLeft = gridArea.Left - textSize.X - ScoreEditorLayout.GridNumberMargin; var textTop = currentY + textSize.Y / 2; context.FillString(textBrush, textFont, text, textLeft, textTop); if (visibleGridCounter >= visibleGridsPerBeat) { visibleGridCounter = 1; } else { ++visibleGridCounter; } } } }
private void DrawNotes(Graphics context, Score score, Rectangle gridArea, ScoreEditorConfig config, ScoreEditorLook look, Rectangle specialNoteArea, float noteStartY, float radius) { if (!score.HasAnyNote) { return; } var unit = look.BarLineSpaceUnit; var startPositionFont = _noteStartPositionFont; var shouldDrawIndicators = look.IndicatorsVisible; var numColumns = config.NumberOfColumns; foreach (var bar in score.Bars) { var numberOfGrids = bar.GetNumberOfGrids(); if (bar.Helper.HasAnyNote) { foreach (var note in bar.Notes) { if (!ScoreEditorLayout.IsNoteVisible(note, gridArea, noteStartY, unit, radius)) { continue; } if (note.Helper.IsGaming) { var x = ScoreEditorLayout.GetNotePositionX(note, gridArea, numColumns); var y = ScoreEditorLayout.GetNotePositionY(note, unit, noteStartY); var h = note.Helper; if (h.IsSlide) { if (h.HasNextFlick) { DrawFlickNote(context, note, x, y, radius, note.Basic.FlickType); } else { DrawSlideNote(context, note, x, y, radius, h.IsSlideMidway); } } else if (h.IsHoldStart) { DrawHoldNote(context, note, x, y, radius); } else { if (note.Basic.FlickType != NoteFlickType.None) { DrawFlickNote(context, note, x, y, radius, note.Basic.FlickType); } else if (h.IsHoldEnd) { DrawHoldNote(context, note, x, y, radius); } else { DrawTapNote(context, note, x, y, radius); } } // Indicators if (shouldDrawIndicators) { if (note.Helper.IsSync) { context.FillCircle(_syncIndicatorBrush, x + radius, y - radius, Definitions.IndicatorRadius); } if (note.Helper.IsHold) { context.FillCircle(_holdIndicatorBrush, x - radius, y + radius, Definitions.IndicatorRadius); } if (note.Helper.IsSlide) { context.FillCircle(_slideIndicatorBrush, x + radius, y + radius, Definitions.IndicatorRadius); } else if (note.Helper.IsFlick) { context.FillCircle(_flickIndicatorBrush, x + radius, y + radius, Definitions.IndicatorRadius); } } // Start position if (note.Basic.StartPosition != note.Basic.FinishPosition) { var startPositionX = x - radius - Definitions.StartPositionFontSize / 2; var startPositionY = y - radius - Definitions.StartPositionFontSize / 2; var text = ((int)note.Basic.StartPosition).ToString(); var textSize = context.MeasureString(startPositionFont, text); context.FillString(_noteCommonFill, startPositionFont, text, startPositionX, startPositionY + textSize.Y); } } else if (note.Helper.IsSpecial) { // Draw a special note (a rectangle) var left = specialNoteArea.Left + radius * 4; var gridY = ScoreEditorLayout.GetNotePositionY(note, unit, noteStartY); var top = gridY - radius * Definitions.SpecialNoteHeightFactor; var width = specialNoteArea.Right - left; var height = radius * 2 * Definitions.SpecialNoteHeightFactor; context.DrawRectangle(_specialNoteStroke, left, top, width, height); context.FillRectangle(_specialNoteFill, left, top, width, height); string specialNoteText; switch (note.Basic.Type) { case NoteType.VariantBpm: specialNoteText = $"BPM: {note.Params.NewBpm:0.00}"; break; default: throw new ArgumentOutOfRangeException(nameof(note.Basic.Type), note.Basic.Type, null); } var rect = context.MeasureString(_specialNoteDescriptionFont, specialNoteText); var textLeft = left + 2; var textTop = gridY + rect.Y / 2; context.FillString(_specialNoteTextBrush, _specialNoteDescriptionFont, specialNoteText, textLeft, textTop); } } } noteStartY -= numberOfGrids * unit; } }
private void DrawNoteConnections(Graphics context, Score score, Rectangle gridArea, ScoreEditorConfig config, ScoreEditorLook look, float scrollOffsetY, float noteStartY, float radius) { if (!score.HasAnyNote) { return; } var unit = look.BarLineSpaceUnit; var numColumns = config.NumberOfColumns; foreach (var bar in score.Bars) { var numberOfGrids = bar.GetNumberOfGrids(); if (bar.Helper.HasAnyNote) { foreach (var note in bar.Notes) { var thisStatus = ScoreEditorLayout.GetNoteOnStageStatus(note, gridArea, noteStartY, unit, radius); var x1 = ScoreEditorLayout.GetNotePositionX(note, gridArea, numColumns); var y1 = ScoreEditorLayout.GetNotePositionY(note, unit, noteStartY); if (note.Helper.HasNextSync && thisStatus == OnStageStatus.OnStage) { var n2 = note.Editor.NextSync; var x2 = ScoreEditorLayout.GetNotePositionX(n2, gridArea, numColumns); // Draw sync line context.DrawLine(_syncLineStroke, x1, y1, x2, y1); } if (note.Helper.IsHoldStart) { var n2 = note.Editor.HoldPair; var s2 = noteStartY; // We promise only calculate the latter note, so this method works. if (note.Basic.Bar != n2.Basic.Bar) { for (var i = note.Basic.Bar.Basic.Index; i < n2.Basic.Bar.Basic.Index; ++i) { s2 -= score.Bars[i].GetNumberOfGrids() * unit; } } var thatStatus = ScoreEditorLayout.GetNoteOnStageStatus(n2, gridArea, s2, unit, radius); if ((int)thisStatus * (int)thatStatus <= 0) { var x2 = ScoreEditorLayout.GetNotePositionX(n2, gridArea, numColumns); var y2 = ScoreEditorLayout.GetNotePositionY(score.Bars, n2.Basic.Bar.Basic.Index, n2.Basic.IndexInGrid, unit, scrollOffsetY); // Draw hold line context.DrawLine(_holdLineStroke, x1, y1, x2, y2); } } if (note.Helper.HasNextFlick) { var n2 = note.Editor.NextFlick; var s2 = noteStartY; if (note.Basic.Bar != n2.Basic.Bar) { for (var i = note.Basic.Bar.Basic.Index; i < n2.Basic.Bar.Basic.Index; ++i) { s2 -= score.Bars[i].GetNumberOfGrids() * unit; } } var thatStatus = ScoreEditorLayout.GetNoteOnStageStatus(n2, gridArea, s2, unit, radius); if ((int)thisStatus * (int)thatStatus <= 0) { var x2 = ScoreEditorLayout.GetNotePositionX(n2, gridArea, numColumns); var y2 = ScoreEditorLayout.GetNotePositionY(score.Bars, n2.Basic.Bar.Basic.Index, n2.Basic.IndexInGrid, unit, scrollOffsetY); // Draw flick line context.DrawLine(_flickLineStroke, x1, y1, x2, y2); } } if (note.Helper.HasNextSlide) { var n2 = note.Editor.NextSlide; var s2 = noteStartY; if (note.Basic.Bar != n2.Basic.Bar) { for (var i = note.Basic.Bar.Basic.Index; i < n2.Basic.Bar.Basic.Index; ++i) { s2 -= score.Bars[i].GetNumberOfGrids() * unit; } } var thatStatus = ScoreEditorLayout.GetNoteOnStageStatus(n2, gridArea, s2, unit, radius); if ((int)thisStatus * (int)thatStatus <= 0) { var x2 = ScoreEditorLayout.GetNotePositionX(n2, gridArea, numColumns); var y2 = ScoreEditorLayout.GetNotePositionY(score.Bars, n2.Basic.Bar.Basic.Index, n2.Basic.IndexInGrid, unit, scrollOffsetY); // Draw slide line context.DrawLine(_slideLineStroke, x1, y1, x2, y2); } } } } noteStartY -= numberOfGrids * unit; } }