From 4b4c2f10e989dc21ac2cd99aadd7b0150a90715b Mon Sep 17 00:00:00 2001 From: Yoshman29 <81191621+YoshiCrafter29@users.noreply.github.com> Date: Wed, 19 Apr 2023 13:50:45 +0200 Subject: [PATCH] algorithm to make the charter faster --- .../funkin/backend/utils/SortedArrayUtil.hx | 49 +++++++++++++++++++ source/funkin/editors/charter/Charter.hx | 19 ++++++- .../editors/charter/CharterNoteGroup.hx | 30 +++++++++--- 3 files changed, 88 insertions(+), 10 deletions(-) create mode 100644 source/funkin/backend/utils/SortedArrayUtil.hx diff --git a/source/funkin/backend/utils/SortedArrayUtil.hx b/source/funkin/backend/utils/SortedArrayUtil.hx new file mode 100644 index 00000000..6a650529 --- /dev/null +++ b/source/funkin/backend/utils/SortedArrayUtil.hx @@ -0,0 +1,49 @@ +package funkin.backend.utils; + +/** + * All types of utils only usable in a sorted array. + */ + class SortedArrayUtil { + /** + * Gets the index of a possible new element of an Array of T using an efficient algorithm. + * @param array Array of T to check in + * @param getVal Function that returns the position value of T + * @return Index + */ + public static inline function binarySearch(array:Array, val:Float, getVal:T->Float):Int { + if (array.length <= 0) return 0; // if the array is empty, it should be equal to zero (the beginning) + if (getVal(array[0]) > val) return 0; // in case its the minimum + if (getVal(array[array.length-1]) < val) return array.length; // in case its the maximum + + // binary search + var iMin:Int = 0; + var iMax:Int = array.length-1; + + var i:Int = 0; + var mid:Float; + while(iMin <= iMax) { + i = Math.floor((iMin + iMax) / 2); + mid = getVal(array[i]); + if (mid < val) + iMin = i+1 + else if (mid > val) + iMax = i-1; + else { + iMin = i; + break; + } + } + return iMin; + } + + /** + * Adds to a sorted array, using binary search. + * @param array Array to add to + * @param val Value to add + * @param getVal Function that returns the value that needs to be sorted + */ + public static inline function addSorted(array:Array, val:T, getVal:T->Float) { + if (val != null) + array.insert(binarySearch(array, getVal(val), getVal), val); + } +} \ No newline at end of file diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index 246c548e..8bd07442 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -399,6 +399,7 @@ class Charter extends UIState { vocals = FlxG.sound.load(Paths.voices(__song, __diff)); vocals.group = FlxG.sound.defaultMusicGroup; + trace("generating notes..."); for(strID=>strL in PlayState.SONG.strumLines) { for(note in strL.notes) { var n = new CharterNote(); @@ -409,6 +410,12 @@ class Charter extends UIState { strumLines.add(new CharterStrumline(strL)); } + trace("sorting notes..."); + notesGroup.sort(function(i, n1, n2) { + if (n1.step == n2.step) + return FlxSort.byValues(FlxSort.ASCENDING, n1.id, n2.id); + return FlxSort.byValues(FlxSort.ASCENDING, n1.step, n2.step); + }); refreshBPMSensitive(); } @@ -522,6 +529,8 @@ class Charter extends UIState { deletes.push(s); } else { s.updatePos(newStep, newID, s.susLength, s.type); + notesGroup.remove(s); + notesGroup.add(s); drags.push({ note: s, @@ -785,8 +794,11 @@ class Charter extends UIState { for(n in changes) n.note.updatePos(n.note.step, n.note.id, n.before, n.note.type); case CNoteDrag(notes, deletes): - for(n in notes) + for(n in notes) { n.note.updatePos(n.oldStep, n.oldID, n.note.susLength, n.note.type); + notesGroup.remove(n.note); + notesGroup.add(n.note); + } for(d in deletes) { notesGroup.add(d); d.revive(); @@ -832,8 +844,11 @@ class Charter extends UIState { for(n in changes) n.note.updatePos(n.note.step, n.note.id, n.after, n.note.type); case CNoteDrag(notes, deletes): - for(n in notes) + for(n in notes) { n.note.updatePos(n.newStep, n.newID, n.note.susLength, n.note.type); + notesGroup.remove(n.note); + notesGroup.add(n.note); + } deleteNotes(deletes, false); selection = [for(n in notes) n.note]; } diff --git a/source/funkin/editors/charter/CharterNoteGroup.hx b/source/funkin/editors/charter/CharterNoteGroup.hx index a65a454e..00cd7e9e 100644 --- a/source/funkin/editors/charter/CharterNoteGroup.hx +++ b/source/funkin/editors/charter/CharterNoteGroup.hx @@ -9,7 +9,6 @@ class CharterNoteGroup extends FlxTypedGroup { var __currentlyLooping:Bool = false; public override function forEach(noteFunc:CharterNote->Void, recursive:Bool = false) { - i = 0; __loopSprite = null; max = FlxG.height / 70 / camera.zoom; @@ -21,19 +20,34 @@ class CharterNoteGroup extends FlxTypedGroup { if (FlxG.state is Charter && !FlxG.sound.music.playing) curStep = cast(FlxG.state, Charter).conductorFollowerSpr.y / 40; - while(i < length) { + var begin = SortedArrayUtil.binarySearch(members, curStep - max, getVarForEach); + var end = SortedArrayUtil.binarySearch(members, curStep + max, getVarForEach); + + for(i in begin...end) { __loopSprite = members[i]; - if (__loopSprite == null || !__loopSprite.exists) { - i++; - continue; - } - if (Math.abs(__loopSprite.step - curStep) - __loopSprite.susLength < max) + if (!cast(FlxG.state, Charter).selection.contains(__loopSprite)) noteFunc(__loopSprite); - i++; } + for(c in cast(FlxG.state, Charter).selection) + noteFunc(c); + __currentlyLooping = oldCur; } + public override function add(v:CharterNote):CharterNote { + SortedArrayUtil.addSorted(members, v, getVar); + return v; + } + + public override function remove(v:CharterNote, force:Bool = true):CharterNote + return super.remove(v, true); + + private static function getVar(n:CharterNote) + return n.step; + + private static function getVarForEach(n:CharterNote) + return n.step + n.susLength; + public override function draw() { @:privateAccess var oldDefaultCameras = FlxCamera._defaultCameras; @:privateAccess if (cameras != null) FlxCamera._defaultCameras = cameras;