diff --git a/ReplayStudio b/ReplayStudio index a0b1f2b6..c6154766 160000 --- a/ReplayStudio +++ b/ReplayStudio @@ -1 +1 @@ -Subproject commit a0b1f2b6628c5296e4306ffd97122727b979b7a9 +Subproject commit c615476693961345d04320e69f6558527ab73a59 diff --git a/src/main/java/com/replaymod/simplepathing/InterpolatorType.java b/src/main/java/com/replaymod/simplepathing/InterpolatorType.java index bc35910e..78993e0d 100644 --- a/src/main/java/com/replaymod/simplepathing/InterpolatorType.java +++ b/src/main/java/com/replaymod/simplepathing/InterpolatorType.java @@ -1,5 +1,6 @@ package com.replaymod.simplepathing; +import com.replaymod.replaystudio.pathing.interpolation.CatmullRomSplineInterpolator; import com.replaymod.replaystudio.pathing.interpolation.CubicSplineInterpolator; import com.replaymod.replaystudio.pathing.interpolation.Interpolator; import com.replaymod.replaystudio.pathing.interpolation.LinearInterpolator; @@ -11,6 +12,7 @@ import java.util.function.Supplier; @AllArgsConstructor public enum InterpolatorType { DEFAULT("default", null, null), + CATMULL_ROM("catmullrom", CatmullRomSplineInterpolator.class, () -> new CatmullRomSplineInterpolator(0.5)), CUBIC("cubic", CubicSplineInterpolator.class, CubicSplineInterpolator::new), LINEAR("linear", LinearInterpolator.class, LinearInterpolator::new); @@ -34,7 +36,7 @@ public enum InterpolatorType { for (InterpolatorType t : values()) { if (t.getI18nName().equals(string)) return t; } - return CUBIC; //the default + return CATMULL_ROM; //the default } public static InterpolatorType fromClass(Class cls) { diff --git a/src/main/java/com/replaymod/simplepathing/SPTimeline.java b/src/main/java/com/replaymod/simplepathing/SPTimeline.java index 49558272..b3cd30ad 100644 --- a/src/main/java/com/replaymod/simplepathing/SPTimeline.java +++ b/src/main/java/com/replaymod/simplepathing/SPTimeline.java @@ -2,6 +2,8 @@ package com.replaymod.simplepathing; import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import com.replaymod.pathing.properties.CameraProperties; @@ -14,6 +16,7 @@ import com.replaymod.replaystudio.pathing.change.CombinedChange; import com.replaymod.replaystudio.pathing.change.SetInterpolator; import com.replaymod.replaystudio.pathing.change.UpdateKeyframeProperties; import com.replaymod.replaystudio.pathing.impl.TimelineImpl; +import com.replaymod.replaystudio.pathing.interpolation.CatmullRomSplineInterpolator; import com.replaymod.replaystudio.pathing.interpolation.CubicSplineInterpolator; import com.replaymod.replaystudio.pathing.interpolation.Interpolator; import com.replaymod.replaystudio.pathing.interpolation.LinearInterpolator; @@ -68,7 +71,7 @@ public class SPTimeline implements PathingRegistry { @Getter private EntityPositionTracker entityTracker; - private InterpolatorType defaultInterpolatorType = InterpolatorType.CUBIC; + private InterpolatorType defaultInterpolatorType = InterpolatorType.fromString("invalid string returns default"); public SPTimeline() { this(createInitialTimeline()); @@ -573,6 +576,11 @@ public class SPTimeline implements PathingRegistry { writer.value("linear"); } else if (interpolator instanceof CubicSplineInterpolator) { writer.value("cubic-spline"); + } else if (interpolator instanceof CatmullRomSplineInterpolator) { + writer.beginObject(); + writer.name("type").value("catmull-rom-spline"); + writer.name("alpha").value(((CatmullRomSplineInterpolator) interpolator).getAlpha()); + writer.endObject(); } else { throw new IOException("Unknown interpolator type: " + interpolator); } @@ -580,12 +588,30 @@ public class SPTimeline implements PathingRegistry { @Override public Interpolator deserializeInterpolator(JsonReader reader) throws IOException { - String type = reader.nextString(); + String type; + JsonObject args; + switch (reader.peek()) { + case STRING: + type = reader.nextString(); + args = null; + break; + case BEGIN_OBJECT: + args = new JsonParser().parse(reader).getAsJsonObject(); + type = args.get("type").getAsString(); + break; + default: + throw new IOException("Unexpected token: " + reader.peek()); + } switch (type) { case "linear": return new LinearInterpolator(); case "cubic-spline": return new CubicSplineInterpolator(); + case "catmull-rom-spline": + if (args == null || !args.has("alpha")) { + throw new IOException("Missing alpha value for catmull-rom-spline."); + } + return new CatmullRomSplineInterpolator(args.get("alpha").getAsDouble()); default: throw new IOException("Unknown interpolation type: " + type); diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java b/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java index 6f3a8577..7fa41072 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java @@ -5,6 +5,7 @@ import com.replaymod.pathing.properties.TimestampProperty; import com.replaymod.replay.ReplayModReplay; import com.replaymod.replaystudio.pathing.change.Change; import com.replaymod.replaystudio.pathing.change.CombinedChange; +import com.replaymod.replaystudio.pathing.interpolation.CatmullRomSplineInterpolator; import com.replaymod.replaystudio.pathing.interpolation.CubicSplineInterpolator; import com.replaymod.replaystudio.pathing.interpolation.Interpolator; import com.replaymod.replaystudio.pathing.interpolation.LinearInterpolator; @@ -317,6 +318,9 @@ public abstract class GuiEditKeyframe> extends Abst removeElement(this.settingsPanel); switch (getInterpolatorTypeNoDefault(type)) { + case CATMULL_ROM: + settingsPanel = new CatmullRomSettingsPanel(); + break; case CUBIC: settingsPanel = new CubicSettingsPanel(); break; @@ -353,6 +357,33 @@ public abstract class GuiEditKeyframe> extends Abst public abstract I createInterpolator(); } + public class CatmullRomSettingsPanel extends SettingsPanel { + public final GuiLabel alphaLabel = new GuiLabel().setColor(Colors.BLACK) + .setI18nText("replaymod.gui.editkeyframe.interpolator.catmullrom.alpha"); + public final GuiNumberField alphaField = new GuiNumberField().setSize(100, 20).setPrecision(5) + .setMinValue(0).setValidateOnFocusChange(true); + + { + setLayout(new HorizontalLayout(HorizontalLayout.Alignment.CENTER)); + addElements(new HorizontalLayout.Data(0.5), alphaLabel, alphaField); + } + + @Override + public void loadSettings(CatmullRomSplineInterpolator interpolator) { + alphaField.setValue(interpolator.getAlpha()); + } + + @Override + public CatmullRomSplineInterpolator createInterpolator() { + return new CatmullRomSplineInterpolator(alphaField.getDouble()); + } + + @Override + protected CatmullRomSettingsPanel getThis() { + return this; + } + } + public class CubicSettingsPanel extends SettingsPanel { @Override diff --git a/src/main/resources/assets/replaymod/lang/en_US.lang b/src/main/resources/assets/replaymod/lang/en_US.lang index e68f9897..c901032b 100644 --- a/src/main/resources/assets/replaymod/lang/en_US.lang +++ b/src/main/resources/assets/replaymod/lang/en_US.lang @@ -292,6 +292,7 @@ replaymod.gui.settings.title=Replay Mod Settings replaymod.gui.settings.interpolation.linear=Linear replaymod.gui.settings.interpolation.cubic=Cubic +replaymod.gui.settings.interpolation.catmullrom=Catmull Rom replaymod.gui.settings.bitrate=Video Bitrate replaymod.gui.settings.framerate=Video Framerate @@ -376,6 +377,9 @@ replaymod.gui.editkeyframe.spec.method.shoulder.smoothness=Path Smoothness replaymod.gui.editkeyframe.interpolator=Interpolator replaymod.gui.editkeyframe.interpolator.default.name=Default Interpolator replaymod.gui.editkeyframe.interpolator.default.desc=Uses the default interpolator defined in the Replay Mod Settings. +replaymod.gui.editkeyframe.interpolator.catmullrom.name=Catmull Rom Spline Interpolator +replaymod.gui.editkeyframe.interpolator.catmullrom.desc=Calculates a catmull rom spline with customizable alpha value for precise tightness and smoothness of the camera path. +replaymod.gui.editkeyframe.interpolator.catmullrom.alpha=Alpha: replaymod.gui.editkeyframe.interpolator.cubic.name=Cubic Spline Interpolator replaymod.gui.editkeyframe.interpolator.cubic.desc=Calculates a cubic equation matrix for all points for a smooth camera path. replaymod.gui.editkeyframe.interpolator.linear.name=Linear Interpolator diff --git a/src/test/java/com/replaymod/simplepathing/SPTimelineTest.java b/src/test/java/com/replaymod/simplepathing/SPTimelineTest.java index b94c7a53..de249b0e 100644 --- a/src/test/java/com/replaymod/simplepathing/SPTimelineTest.java +++ b/src/test/java/com/replaymod/simplepathing/SPTimelineTest.java @@ -5,6 +5,7 @@ import com.replaymod.pathing.properties.CameraProperties; import com.replaymod.pathing.properties.SpectatorProperty; import com.replaymod.pathing.properties.TimestampProperty; import com.replaymod.replaystudio.pathing.change.Change; +import com.replaymod.replaystudio.pathing.interpolation.CatmullRomSplineInterpolator; import com.replaymod.replaystudio.pathing.interpolation.Interpolator; import com.replaymod.replaystudio.pathing.interpolation.LinearInterpolator; import com.replaymod.replaystudio.pathing.path.Keyframe; @@ -510,6 +511,37 @@ public class SPTimelineTest { assertValidInterpolators(SPPath.POSITION, 4); } + @Test(expected = IllegalArgumentException.class) + public void testSetDefaultInterpolatorDifferentType() { + addPosition(0, 0); + addPosition(1, 1); + impl.setDefaultInterpolator(new CatmullRomSplineInterpolator(42)); + } + + @Test + public void testSetDefaultInterpolatorSameType() { + impl.setDefaultInterpolatorType(InterpolatorType.CATMULL_ROM); + addPosition(0, 0); + addSpectator(1, 1); + addSpectator(2, 2); + addPosition(3, 3); + addPosition(4, 3); + impl.setDefaultInterpolator(new CatmullRomSplineInterpolator(42)); + assertValidInterpolators(SPPath.POSITION, 3); + assertIsCatmullRom(0, 42); + assertIsCatmullRom(2, 42); + assertIsCatmullRom(3, 42); + } + + private void assertIsCatmullRom(int index, double alpha) { + String str = prettyPrintInterpolators(impl, SPPath.POSITION); + Interpolator interpolator = Iterables.get(impl.getPositionPath().getSegments(), index).getInterpolator(); + assertTrue("Expected segment " + index + " to be catmull rom interpolator: " + str, + interpolator instanceof CatmullRomSplineInterpolator); + assertTrue("Expected interpolator of segment segment " + index + " to have alpha " + alpha + ": " + str, + ((CatmullRomSplineInterpolator) interpolator).getAlpha() == 42); + } + private void addPosition(int time, int expectedNumberOfInterpolators) { impl.addPositionKeyframe(time, 1, 2, 3, 4, 5, 6, -1); assertNotNull(impl.getKeyframe(SPPath.POSITION, time));