Skip to content

Commit

Permalink
Merge pull request #1490 from andy840119/apply-deep-clone-in-some-hit…
Browse files Browse the repository at this point in the history
…-object

Implement `IDeepCloneable<T>` for some hit-object for better copy experience.
  • Loading branch information
andy840119 authored Jul 31, 2022
2 parents e5ded23 + d6dca60 commit e9cf484
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public void TestCombine()

var combinedNote = actualNotes.First();
Assert.AreEqual("カラ", combinedNote.Text);
Assert.AreEqual(null, combinedNote.RubyText);
Assert.AreEqual("から", combinedNote.RubyText);
Assert.AreEqual(1000, combinedNote.StartTime);
Assert.AreEqual(1000, combinedNote.Duration);
});
Expand Down
84 changes: 84 additions & 0 deletions osu.Game.Rulesets.Karaoke.Tests/Objects/LyricTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) andy840119 <[email protected]>. Licensed under the GPL Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Collections.Generic;
using System.Globalization;
using NUnit.Framework;
using osu.Game.Rulesets.Karaoke.Objects;
using osu.Game.Rulesets.Karaoke.Objects.Types;
using osu.Game.Rulesets.Karaoke.Tests.Asserts;
using osu.Game.Rulesets.Karaoke.Tests.Helper;

namespace osu.Game.Rulesets.Karaoke.Tests.Objects
{
public class LyricTest
{
[TestCase]
public void TestClone()
{
var referencedLyric = new Lyric();

var lyric = new Lyric
{
ID = 1,
Text = "カラオケ",
TimeTags = TestCaseTagHelper.ParseTimeTags(new[] { "[0,start]:1000", "[1,start]:2000", "[2,start]:3000", "[3,start]:4000", "[3,end]:5000" }),
RubyTags = TestCaseTagHelper.ParseRubyTags(new[] { "[0,1]:か", "[1,2]:ら", "[2,3]:お", "[3,4]:け" }),
RomajiTags = TestCaseTagHelper.ParseRomajiTags(new[] { "[0,2]:ka", "[2,4]:ra", "[4,5]:o", "[5,7]:ke" }),
StartTime = 1000,
Duration = 4000,
Singers = new[] { 1, 2 },
Translates = new Dictionary<CultureInfo, string>
{
{ new CultureInfo("en-US"), "karaoke" }
},
Language = new CultureInfo("ja-JP"),
Order = 1,
Lock = LockState.None,
ReferenceLyric = referencedLyric
};

var clonedLyric = lyric.DeepClone();

Assert.AreEqual(clonedLyric.ID, lyric.ID);

Assert.AreNotSame(clonedLyric.TextBindable, lyric.TextBindable);
Assert.AreEqual(clonedLyric.Text, lyric.Text);

Assert.AreNotSame(clonedLyric.TimeTagsVersion, lyric.TimeTagsVersion);
Assert.AreNotSame(clonedLyric.TimeTagsBindable, lyric.TimeTagsBindable);
TimeTagAssert.ArePropertyEqual(clonedLyric.TimeTags, lyric.TimeTags);

Assert.AreNotSame(clonedLyric.RubyTagsVersion, lyric.RubyTagsVersion);
Assert.AreNotSame(clonedLyric.RubyTagsBindable, lyric.RubyTagsBindable);
TextTagAssert.ArePropertyEqual(clonedLyric.RubyTags, lyric.RubyTags);

Assert.AreNotSame(clonedLyric.RomajiTagsVersion, lyric.RomajiTagsVersion);
Assert.AreNotSame(clonedLyric.RomajiTagsBindable, lyric.RomajiTagsBindable);
TextTagAssert.ArePropertyEqual(clonedLyric.RomajiTags, lyric.RomajiTags);

Assert.AreNotSame(clonedLyric.StartTimeBindable, lyric.StartTimeBindable);
Assert.AreEqual(clonedLyric.StartTime, lyric.StartTime);

Assert.AreEqual(clonedLyric.Duration, lyric.Duration);

Assert.AreNotSame(clonedLyric.SingersBindable, lyric.SingersBindable);
CollectionAssert.AreEquivalent(clonedLyric.Singers, lyric.Singers);

Assert.AreNotSame(clonedLyric.TranslateTextBindable, lyric.TranslateTextBindable);
CollectionAssert.AreEquivalent(clonedLyric.Translates, lyric.Translates);

Assert.AreNotSame(clonedLyric.LanguageBindable, lyric.LanguageBindable);
Assert.AreEqual(clonedLyric.Language, lyric.Language);

Assert.AreNotSame(clonedLyric.OrderBindable, lyric.OrderBindable);
Assert.AreEqual(clonedLyric.Order, lyric.Order);

Assert.AreNotSame(clonedLyric.LockBindable, lyric.LockBindable);
Assert.AreEqual(clonedLyric.Lock, lyric.Lock);

Assert.AreNotSame(clonedLyric.ReferenceLyricBindable, lyric.ReferenceLyricBindable);
Assert.AreSame(clonedLyric.ReferenceLyric, lyric.ReferenceLyric);
}
}
}
52 changes: 52 additions & 0 deletions osu.Game.Rulesets.Karaoke.Tests/Objects/NoteTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) andy840119 <[email protected]>. Licensed under the GPL Licence.
// See the LICENCE file in the repository root for full licence text.

using NUnit.Framework;
using osu.Game.Rulesets.Karaoke.Objects;

namespace osu.Game.Rulesets.Karaoke.Tests.Objects
{
public class NoteTest
{
[TestCase]
public void TestClone()
{
var parentLyric = new Lyric();

var note = new Note
{
Text = "ノート",
RubyText = "Note",
Display = true,
StartTime = 1000,
Duration = 500,
ParentLyric = parentLyric
};

var clonedNote = note.DeepClone();

Assert.AreNotSame(clonedNote.TextBindable, note.TextBindable);
Assert.AreEqual(clonedNote.Text, note.Text);

Assert.AreNotSame(clonedNote.RubyTextBindable, note.RubyTextBindable);
Assert.AreEqual(clonedNote.RubyText, note.RubyText);

Assert.AreNotSame(clonedNote.DisplayBindable, note.DisplayBindable);
Assert.AreEqual(clonedNote.Display, note.Display);

Assert.AreNotSame(clonedNote.ToneBindable, note.ToneBindable);
Assert.AreEqual(clonedNote.Tone, note.Tone);

Assert.AreNotSame(clonedNote.StartTimeBindable, note.StartTimeBindable);
Assert.AreEqual(clonedNote.StartTime, note.StartTime);

Assert.AreEqual(clonedNote.Duration, note.Duration);

Assert.AreEqual(clonedNote.StartIndex, note.StartIndex);

Assert.AreEqual(clonedNote.EndIndex, note.EndIndex);

Assert.AreSame(clonedNote.ParentLyric, note.ParentLyric);
}
}
}
14 changes: 13 additions & 1 deletion osu.Game.Rulesets.Karaoke/Objects/Lyric.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,18 @@
using osu.Game.Extensions;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Karaoke.Beatmaps;
using osu.Game.Rulesets.Karaoke.IO.Serialization;
using osu.Game.Rulesets.Karaoke.Judgements;
using osu.Game.Rulesets.Karaoke.Objects.Types;
using osu.Game.Rulesets.Karaoke.Scoring;
using osu.Game.Rulesets.Karaoke.Utils;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osu.Game.Utils;

namespace osu.Game.Rulesets.Karaoke.Objects
{
public class Lyric : KaraokeHitObject, IHasDuration, IHasSingers, IHasOrder, IHasLock, IHasPrimaryKey
public class Lyric : KaraokeHitObject, IHasDuration, IHasSingers, IHasOrder, IHasLock, IHasPrimaryKey, IDeepCloneable<Lyric>
{
/// <summary>
/// Primary key.
Expand Down Expand Up @@ -291,5 +293,15 @@ public void InitialWorkingTime()
}

protected override HitWindows CreateHitWindows() => new KaraokeLyricHitWindows();

public Lyric DeepClone()
{
string serializeString = JsonConvert.SerializeObject(this, KaraokeJsonSerializableExtensions.CreateGlobalSettings());
var lyric = JsonConvert.DeserializeObject<Lyric>(serializeString, KaraokeJsonSerializableExtensions.CreateGlobalSettings())!;

lyric.ReferenceLyric = ReferenceLyric;

return lyric;
}
}
}
14 changes: 12 additions & 2 deletions osu.Game.Rulesets.Karaoke/Objects/Note.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@

using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Karaoke.Configuration;
using osu.Game.Rulesets.Karaoke.Judgements;
using osu.Game.Rulesets.Karaoke.Objects.Types;
using osu.Game.Rulesets.Karaoke.Scoring;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using osu.Game.Utils;

namespace osu.Game.Rulesets.Karaoke.Objects
{
public class Note : KaraokeHitObject, IHasDuration, IHasText
public class Note : KaraokeHitObject, IHasDuration, IHasText, IDeepCloneable<Note>
{
[JsonIgnore]
public readonly Bindable<string> TextBindable = new();
Expand Down Expand Up @@ -74,7 +76,6 @@ public virtual Tone Tone
/// <summary>
/// Duration
/// </summary>
[JsonIgnore]
public double Duration { get; set; }

/// <summary>
Expand Down Expand Up @@ -110,5 +111,14 @@ public Lyric ParentLyric
public override Judgement CreateJudgement() => new KaraokeNoteJudgement();

protected override HitWindows CreateHitWindows() => new KaraokeNoteHitWindows();

public Note DeepClone()
{
string serializeString = this.Serialize();
var note = serializeString.Deserialize<Note>();
note.ParentLyric = ParentLyric;

return note;
}
}
}
34 changes: 13 additions & 21 deletions osu.Game.Rulesets.Karaoke/Utils/LyricsUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,28 +60,20 @@ public static Tuple<Lyric, Lyric> SplitLyric(Lyric lyric, int splitIndex)
}
}

var firstLyric = new Lyric
{
Text = lyric.Text[..splitIndex],
TimeTags = firstTimeTag.ToArray(),
RubyTags = lyric.RubyTags.Where(x => x.StartIndex < splitIndex && x.EndIndex <= splitIndex).ToArray(),
RomajiTags = lyric.RomajiTags.Where(x => x.StartIndex < splitIndex && x.EndIndex <= splitIndex).ToArray(),
// todo : should implement time and duration
Singers = lyric.Singers,
Language = lyric.Language,
};

// todo : should implement time and duration
var firstLyric = lyric.DeepClone();
firstLyric.Text = lyric.Text[..splitIndex];
firstLyric.TimeTags = firstTimeTag.ToArray();
firstLyric.RubyTags = lyric.RubyTags.Where(x => x.StartIndex < splitIndex && x.EndIndex <= splitIndex).ToArray();
firstLyric.RomajiTags = lyric.RomajiTags.Where(x => x.StartIndex < splitIndex && x.EndIndex <= splitIndex).ToArray();

// todo : should implement time and duration
string secondLyricText = lyric.Text[splitIndex..];
var secondLyric = new Lyric
{
Text = secondLyricText,
TimeTags = shiftingTimeTag(secondTimeTag.ToArray(), -splitIndex),
RubyTags = shiftingTextTag(lyric.RubyTags.Where(x => x.StartIndex >= splitIndex && x.EndIndex > splitIndex).ToArray(), secondLyricText, -splitIndex),
RomajiTags = shiftingTextTag(lyric.RomajiTags.Where(x => x.StartIndex >= splitIndex && x.EndIndex > splitIndex).ToArray(), secondLyricText, -splitIndex),
// todo : should implement time and duration
Singers = lyric.Singers,
Language = lyric.Language,
};
var secondLyric = lyric.DeepClone();
secondLyric.Text = secondLyricText;
secondLyric.TimeTags = shiftingTimeTag(secondTimeTag.ToArray(), -splitIndex);
secondLyric.RubyTags = shiftingTextTag(lyric.RubyTags.Where(x => x.StartIndex >= splitIndex && x.EndIndex > splitIndex).ToArray(), secondLyricText, -splitIndex);
secondLyric.RomajiTags = shiftingTextTag(lyric.RomajiTags.Where(x => x.StartIndex >= splitIndex && x.EndIndex > splitIndex).ToArray(), secondLyricText, -splitIndex);

return new Tuple<Lyric, Lyric>(firstLyric, secondLyric);
}
Expand Down
20 changes: 8 additions & 12 deletions osu.Game.Rulesets.Karaoke/Utils/NoteUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,14 @@ public static Note SliceNote(Note note, double startPercentage, double durationP
return CopyByTime(note, startTime, duration);
}

public static Note CopyByTime(Note originNote, double startTime, double duration) =>
new()
{
StartTime = startTime,
Duration = duration,
StartIndex = originNote.StartIndex,
EndIndex = originNote.EndIndex,
Text = originNote.Text,
Display = originNote.Display,
Tone = originNote.Tone,
ParentLyric = originNote.ParentLyric
};
public static Note CopyByTime(Note originNote, double startTime, double duration)
{
var note = originNote.DeepClone();
note.StartTime = startTime;
note.Duration = duration;

return note;
}

/// <summary>
/// Get the display text while gameplay or in editor.
Expand Down

0 comments on commit e9cf484

Please sign in to comment.