WIP implementation of no stacking when pasting

This commit is contained in:
Hyper_ 2024-10-06 21:10:46 -03:00 committed by Hyper_
parent 21cee45cbb
commit 7396ad5508
3 changed files with 72 additions and 6 deletions

View file

@ -203,6 +203,12 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
*/ */
public static final NOTE_SELECT_BUTTON_HEIGHT:Int = 24; public static final NOTE_SELECT_BUTTON_HEIGHT:Int = 24;
/**
* How "close" in milliseconds two notes have to be to be considered as stacked.
* TODO: This should probably be turned into a modifiable value
*/
public static final STACK_NOTE_THRESHOLD:Int = 20;
/** /**
* The amount of padding between the menu bar and the chart grid when fully scrolled up. * The amount of padding between the menu bar and the chart grid when fully scrolled up.
*/ */
@ -3663,7 +3669,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
} }
} }
var stackedNotes:Array<SongNoteData> = NoteDataFilter.filterStackedNotes(displayedNoteData, 5); var stackedNotes = NoteDataFilter.listStackedNotes(currentSongChartNoteData, STACK_NOTE_THRESHOLD);
// Add events that are now visible. // Add events that are now visible.
for (eventData in currentSongChartEventData) for (eventData in currentSongChartEventData)
@ -3801,6 +3807,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
} }
else else
{ {
// TODO: Move this to a function like isNoteSelected does
if (noteSprite.noteData != null && stackedNotes.contains(noteSprite.noteData)) if (noteSprite.noteData != null && stackedNotes.contains(noteSprite.noteData))
{ {
// TODO: Maybe use another way to display these notes // TODO: Maybe use another way to display these notes
@ -3864,6 +3871,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
// Sort the events DESCENDING. This keeps the sustain behind the associated note. // Sort the events DESCENDING. This keeps the sustain behind the associated note.
renderedEvents.sort(FlxSort.byY, FlxSort.DESCENDING); // TODO: .group.insertionSort() renderedEvents.sort(FlxSort.byY, FlxSort.DESCENDING); // TODO: .group.insertionSort()
} }
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.H)
{
// performCommand(new RemoveNotesCommand(stackedNotes));
}
} }
/** /**

View file

@ -5,6 +5,8 @@ import funkin.data.song.SongData.SongNoteData;
import funkin.data.song.SongDataUtils; import funkin.data.song.SongDataUtils;
import funkin.data.song.SongDataUtils.SongClipboardItems; import funkin.data.song.SongDataUtils.SongClipboardItems;
using funkin.ui.debug.charting.util.NoteDataFilter;
/** /**
* A command which inserts the contents of the clipboard into the chart editor. * A command which inserts the contents of the clipboard into the chart editor.
*/ */
@ -41,7 +43,11 @@ class PasteItemsCommand implements ChartEditorCommand
addedEvents = SongDataUtils.offsetSongEventData(currentClipboard.events, Std.int(targetTimestamp)); addedEvents = SongDataUtils.offsetSongEventData(currentClipboard.events, Std.int(targetTimestamp));
addedEvents = SongDataUtils.clampSongEventData(addedEvents, 0.0, msCutoff); addedEvents = SongDataUtils.clampSongEventData(addedEvents, 0.0, msCutoff);
state.currentSongChartNoteData = state.currentSongChartNoteData.concat(addedNotes); // If a warning should appear when pasting a note on top of another
// TODO: Should events also not be allowed to stack?
var shouldWarn = false;
state.currentSongChartNoteData = state.currentSongChartNoteData.concatFilterStackedNotes(addedNotes, ChartEditorState.STACK_NOTE_THRESHOLD);
state.currentSongChartEventData = state.currentSongChartEventData.concat(addedEvents); state.currentSongChartEventData = state.currentSongChartEventData.concat(addedEvents);
state.currentNoteSelection = addedNotes.copy(); state.currentNoteSelection = addedNotes.copy();
state.currentEventSelection = addedEvents.copy(); state.currentEventSelection = addedEvents.copy();

View file

@ -15,7 +15,7 @@ class NoteDataFilter
* @param threshold Threshold in ms * @param threshold Threshold in ms
* @return Stacked notes * @return Stacked notes
*/ */
public static function filterStackedNotes(notes:Array<SongNoteData>, threshold:Float):Array<SongNoteData> public static function listStackedNotes(notes:Array<SongNoteData>, threshold:Float):Array<SongNoteData>
{ {
var stackedNotes:Array<SongNoteData> = []; var stackedNotes:Array<SongNoteData> = [];
@ -49,16 +49,16 @@ class NoteDataFilter
var noteI:SongNoteData = chunk[i]; var noteI:SongNoteData = chunk[i];
var noteJ:SongNoteData = chunk[j]; var noteJ:SongNoteData = chunk[j];
if (noteI.getStrumlineIndex() == noteJ.getStrumlineIndex() && noteI.getDirection() == noteJ.getDirection()) if (canNotesStack(noteI, noteJ))
{ {
if (Math.abs(noteJ.time - noteI.time) <= threshold) if (Math.abs(noteJ.time - noteI.time) <= threshold)
{ {
if (!stackedNotes.contains(noteI)) if (!stackedNotes.fastContains(noteI))
{ {
stackedNotes.push(noteI); stackedNotes.push(noteI);
} }
if (!stackedNotes.contains(noteJ)) if (!stackedNotes.fastContains(noteJ))
{ {
stackedNotes.push(noteJ); stackedNotes.push(noteJ);
} }
@ -70,4 +70,52 @@ class NoteDataFilter
return stackedNotes; return stackedNotes;
} }
/**
* Tries to concatenate two arrays of notes together but skips notes from `notesB` that overlap notes from `noteA`.
* @param notesA An array of notes into which `notesB` will be concatenated.
* @param notesB Another array of notes that will be concated into `input`.
* @param threshold Threshold in ms
* @param modifyB If `true` modifies `notesB` in-place by removing the notes that overlap notes from `notesA`.
* @return Array<SongNoteData>
*/
public static function concatFilterStackedNotes(notesA:Array<SongNoteData>, notesB:Array<SongNoteData>, threshold:Float,
modifyB:Bool = false):Array<SongNoteData>
{
// TODO: Maybe this whole function should be moved to SongNoteDataArrayTools
var result:Array<SongNoteData> = notesA.copy();
for (noteB in notesB)
{
var overlaps:Bool = false;
for (noteA in notesA)
{
if (canNotesStack(noteA, noteB))
{
if (Math.abs(noteA.time - noteB.time) < threshold)
{
overlaps = true;
break;
}
}
}
if (!overlaps)
{
result.push(noteB);
if (modifyB) notesB.remove(noteB);
}
}
return result;
}
/**
* @return Returns `true` if both notes are on the same strumline and have the same direction
*/
public static inline function canNotesStack(noteA:SongNoteData, noteB:SongNoteData):Bool
{
return noteA.getStrumlineIndex() == noteB.getStrumlineIndex() && noteA.getDirection() == noteB.getDirection();
}
} }