diff --git a/gradle.properties b/gradle.properties index de2df70..553a1e8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ minecraft_version = 1.19.4 yarn_mappings = 1.19.4+build.1 loader_version = 0.14.18 -mod_version = 2.0.6 +mod_version = 2.0.7 maven_group = net.krlite archives_base_name = equator diff --git a/src/main/java/net/krlite/equator/test/CanvasScreen.java b/src/main/java/net/krlite/equator/test/CanvasScreen.java index 5377267..473d5bb 100644 --- a/src/main/java/net/krlite/equator/test/CanvasScreen.java +++ b/src/main/java/net/krlite/equator/test/CanvasScreen.java @@ -16,9 +16,9 @@ public CanvasScreen() { private final Interpolation interpolation = new Interpolation(0, 1); { - Interpolation.InterpolationCallbacks.Complete.EVENT.register(i -> i.targetValue(1 - i.targetValue())); + Interpolation.Callbacks.Complete.EVENT.register(i -> i.targetValue(1 - i.targetValue())); - InputEvents.InputCallbacks.Mouse.EVENT.register((event, button, mods) -> { + InputEvents.Callbacks.Mouse.EVENT.register((event, button, mods) -> { if (event == InputEvents.InputEvent.MOUSE_PRESSED) interpolation.switchPauseResume(); }); diff --git a/src/main/java/net/krlite/equator/util/InputEvents.java b/src/main/java/net/krlite/equator/util/InputEvents.java index 632306c..545d0c0 100644 --- a/src/main/java/net/krlite/equator/util/InputEvents.java +++ b/src/main/java/net/krlite/equator/util/InputEvents.java @@ -8,7 +8,7 @@ import java.io.File; public class InputEvents { - public interface InputCallbacks { + public interface Callbacks { interface Mouse { Event EVENT = EventFactory.createArrayBacked(Mouse.class, (listeners) -> (event, button, mods) -> { for (Mouse listener : listeners) { @@ -153,10 +153,10 @@ public static void initMouseCallback(long window) { public void invoke(long window, int button, int action, int mods) { System.out.println("Mouse button " + button + " " + (action == GLFW.GLFW_PRESS ? "pressed" : "released") + " at glfw time " + GLFW.glfwGetTime() + " at system time " + System.currentTimeMillis()); if (action == GLFW.GLFW_PRESS) { - InputCallbacks.Mouse.EVENT.invoker().onMouse(InputEvent.MOUSE_PRESSED, button, mods); + Callbacks.Mouse.EVENT.invoker().onMouse(InputEvent.MOUSE_PRESSED, button, mods); } else if (action == GLFW.GLFW_RELEASE) { - InputCallbacks.Mouse.EVENT.invoker().onMouse(InputEvent.MOUSE_RELEASED, button, mods); + Callbacks.Mouse.EVENT.invoker().onMouse(InputEvent.MOUSE_RELEASED, button, mods); } } }; @@ -168,8 +168,8 @@ public static void initMouseScrollCallback(long window) { GLFWScrollCallback mouseScrollCallback = new GLFWScrollCallback() { @Override public void invoke(long window, double xOffset, double yOffset) { - InputCallbacks.Mouse.EVENT.invoker().onMouse(InputEvent.MOUSE_SCROLLED, GLFW.GLFW_MOUSE_BUTTON_MIDDLE, 0); - InputCallbacks.MouseScroll.EVENT.invoker().onMouseScroll(xOffset, yOffset); + Callbacks.Mouse.EVENT.invoker().onMouse(InputEvent.MOUSE_SCROLLED, GLFW.GLFW_MOUSE_BUTTON_MIDDLE, 0); + Callbacks.MouseScroll.EVENT.invoker().onMouseScroll(xOffset, yOffset); } }; @@ -180,10 +180,10 @@ public static void initCursorPositionCallback(long window) { GLFWCursorPosCallback cursorPositionCallback = new GLFWCursorPosCallback() { @Override public void invoke(long window, double x, double y) { - InputCallbacks.CursorPosition.EVENT.invoker().onCursorPosition(x, y); + Callbacks.CursorPosition.EVENT.invoker().onCursorPosition(x, y); for (int button = 0; button < GLFW.GLFW_MOUSE_BUTTON_LAST; button++) { if (isMouseDown(button)) { - InputCallbacks.MouseDrag.EVENT.invoker().onMouseDrag(button, x, y); + Callbacks.MouseDrag.EVENT.invoker().onMouseDrag(button, x, y); } } } @@ -196,7 +196,7 @@ public static void initCursorEnterCallback(long window) { GLFWCursorEnterCallback cursorEnterCallback = new GLFWCursorEnterCallback() { @Override public void invoke(long window, boolean entered) { - InputCallbacks.CursorEnter.EVENT.invoker().onCursorEnter(entered); + Callbacks.CursorEnter.EVENT.invoker().onCursorEnter(entered); } }; @@ -232,13 +232,13 @@ public static void initKeyboardCallback(long window) { @Override public void invoke(long window, int key, int scancode, int action, int mods) { if (action == GLFW.GLFW_PRESS) { - InputCallbacks.Keyboard.EVENT.invoker().onKeyboard(InputEvent.KEY_PRESSED, key, scancode, mods); + Callbacks.Keyboard.EVENT.invoker().onKeyboard(InputEvent.KEY_PRESSED, key, scancode, mods); } else if (action == GLFW.GLFW_RELEASE) { - InputCallbacks.Keyboard.EVENT.invoker().onKeyboard(InputEvent.KEY_RELEASED, key, scancode, mods); + Callbacks.Keyboard.EVENT.invoker().onKeyboard(InputEvent.KEY_RELEASED, key, scancode, mods); } else if (action == GLFW.GLFW_REPEAT) { - InputCallbacks.Keyboard.EVENT.invoker().onKeyboard(InputEvent.KEY_TYPED, key, scancode, mods); + Callbacks.Keyboard.EVENT.invoker().onKeyboard(InputEvent.KEY_TYPED, key, scancode, mods); } } }; @@ -310,7 +310,7 @@ public static WindowStateMaximize fromBoolean(boolean maximized) { windowStateIconify = WindowStateIconify.fromBoolean(GLFW.glfwGetWindowAttrib(GLFW.glfwGetCurrentContext(), GLFW.GLFW_ICONIFIED) == GLFW.GLFW_TRUE); windowStateMaximize = WindowStateMaximize.fromBoolean(GLFW.glfwGetWindowAttrib(GLFW.glfwGetCurrentContext(), GLFW.GLFW_MAXIMIZED) == GLFW.GLFW_TRUE); - InputCallbacks.Window.EVENT.register((event) -> { + Callbacks.Window.EVENT.register((event) -> { if (event == InputEvent.WINDOW_CLOSED) { windowStateVisible = WindowStateVisible.fromBoolean(false); } @@ -371,7 +371,7 @@ public static void initWindowCloseCallback(long window) { GLFWWindowCloseCallback windowCloseCallback = new GLFWWindowCloseCallback() { @Override public void invoke(long window) { - InputCallbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_CLOSED); + Callbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_CLOSED); } }; @@ -383,10 +383,10 @@ public static void initWindowFocusCallback(long window) { @Override public void invoke(long window, boolean focused) { if (focused) { - InputCallbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_GAINED_FOCUS); + Callbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_GAINED_FOCUS); } else { - InputCallbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_LOST_FOCUS); + Callbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_LOST_FOCUS); } } }; @@ -399,10 +399,10 @@ public static void initWindowIconifyCallback(long window) { @Override public void invoke(long window, boolean iconified) { if (iconified) { - InputCallbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_ICONIFIED); + Callbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_ICONIFIED); } else { - InputCallbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_DEICONIFIED); + Callbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_DEICONIFIED); } } }; @@ -415,9 +415,9 @@ public static void initWindowMaximizeCallback(long window) { @Override public void invoke(long window, boolean maximized) { if (maximized) - InputCallbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_MAXIMIZED); + Callbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_MAXIMIZED); else - InputCallbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_DEMAXIMIZED); + Callbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_DEMAXIMIZED); } }; @@ -428,8 +428,8 @@ public static void initWindowPositionCallback(long window) { GLFWWindowPosCallback windowPositionCallback = new GLFWWindowPosCallback() { @Override public void invoke(long window, int x, int y) { - InputCallbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_STATE_CHANGED); - InputCallbacks.WindowPosition.EVENT.invoker().onWindowPosition(x, y); + Callbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_STATE_CHANGED); + Callbacks.WindowPosition.EVENT.invoker().onWindowPosition(x, y); } }; @@ -440,8 +440,8 @@ public static void initWindowSizeCallback(long window) { GLFWWindowSizeCallback windowSizeCallback = new GLFWWindowSizeCallback() { @Override public void invoke(long window, int width, int height) { - InputCallbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_RESIZED); - InputCallbacks.WindowSize.EVENT.invoker().onWindowSize(width, height); + Callbacks.Window.EVENT.invoker().onWindow(InputEvent.WINDOW_RESIZED); + Callbacks.WindowSize.EVENT.invoker().onWindowSize(width, height); } }; diff --git a/src/main/java/net/krlite/equator/visual/animation/Animation.java b/src/main/java/net/krlite/equator/visual/animation/Animation.java index 85584a1..9938f16 100644 --- a/src/main/java/net/krlite/equator/visual/animation/Animation.java +++ b/src/main/java/net/krlite/equator/visual/animation/Animation.java @@ -14,10 +14,10 @@ import java.util.function.UnaryOperator; public class Animation implements Runnable { - public interface AnimationCallbacks { + public interface Callbacks { interface Start { - Event EVENT = EventFactory.createArrayBacked(AnimationCallbacks.Start.class, (listeners) -> (animation) -> { - for (AnimationCallbacks.Start listener : listeners) { + Event EVENT = EventFactory.createArrayBacked(Callbacks.Start.class, (listeners) -> (animation) -> { + for (Callbacks.Start listener : listeners) { listener.onStart(animation); } }); @@ -44,6 +44,26 @@ interface Repeat { void onRepetition(Animation animation); } + + interface StartFrame { + Event EVENT = EventFactory.createArrayBacked(StartFrame.class, (listeners) -> (animation) -> { + for (StartFrame listener : listeners) { + listener.onFrameStart(animation); + } + }); + + void onFrameStart(Animation animation); + } + + interface EndFrame { + Event EVENT = EventFactory.createArrayBacked(EndFrame.class, (listeners) -> (animation) -> { + for (EndFrame listener : listeners) { + listener.onFrameEnd(animation); + } + }); + + void onFrameEnd(Animation animation); + } } public Animation(double startValue, double endValue, long duration, TimeUnit timeUnit, Slice slice, boolean repeat, @Nullable Runnable callback) { @@ -164,14 +184,16 @@ private void future(@Nullable ScheduledFuture future) { */ @Override public void run() { + Callbacks.StartFrame.EVENT.invoker().onFrameStart(this); + if (isCompleted()) { if (repeat()) { reset(); - AnimationCallbacks.Repeat.EVENT.invoker().onRepetition(this); + Callbacks.Repeat.EVENT.invoker().onRepetition(this); } else { stop(); - AnimationCallbacks.Complete.EVENT.invoker().onCompletion(this); + Callbacks.Complete.EVENT.invoker().onCompletion(this); } } else { progress.addAndGet(period()); @@ -180,12 +202,14 @@ public void run() { if (callback() != null) { executor.execute(callback()); } + + Callbacks.EndFrame.EVENT.invoker().onFrameEnd(this); } public void start() { if (isStopped()) { reset(); - AnimationCallbacks.Start.EVENT.invoker().onStart(this); + Callbacks.Start.EVENT.invoker().onStart(this); future(AnimationThreadPoolExecutor.join(this, 0)); } } @@ -217,7 +241,7 @@ public void reset() { public void restart() { if (isRunning()) { - AnimationCallbacks.Start.EVENT.invoker().onStart(this); + Callbacks.Start.EVENT.invoker().onStart(this); } stop(); start(); diff --git a/src/main/java/net/krlite/equator/visual/animation/Interpolation.java b/src/main/java/net/krlite/equator/visual/animation/Interpolation.java index ab27113..0b6c9ae 100644 --- a/src/main/java/net/krlite/equator/visual/animation/Interpolation.java +++ b/src/main/java/net/krlite/equator/visual/animation/Interpolation.java @@ -6,7 +6,6 @@ import net.krlite.equator.math.algebra.Theory; import org.jetbrains.annotations.Nullable; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledFuture; @@ -14,7 +13,7 @@ import java.util.concurrent.atomic.AtomicReference; public class Interpolation implements Runnable { - public interface InterpolationCallbacks { + public interface Callbacks { interface Start { Event EVENT = EventFactory.createArrayBacked(Start.class, (listeners) -> (interpolation) -> { for (Start listener : listeners) { @@ -34,13 +33,32 @@ interface Complete { void onCompletion(Interpolation interpolation); } + + interface StartFrame { + Event EVENT = EventFactory.createArrayBacked(StartFrame.class, (listeners) -> (interpolation) -> { + for (StartFrame listener : listeners) { + listener.onFrameStart(interpolation); + } + }); + + void onFrameStart(Interpolation interpolation); + } + + interface EndFrame { + Event EVENT = EventFactory.createArrayBacked(EndFrame.class, (listeners) -> (interpolation) -> { + for (EndFrame listener : listeners) { + listener.onFrameEnd(interpolation); + } + }); + + void onFrameEnd(Interpolation interpolation); + } } - public Interpolation(double originValue, double targetValue, double approximatedTimeSteps, boolean pauseAtStart, @Nullable Runnable callback) { + public Interpolation(double originValue, double targetValue, double approximatedTimeSteps, boolean pauseAtStart) { this.targetValue = new AtomicDouble(targetValue); this.value = new AtomicDouble(originValue); this.speed = new AtomicDouble(Theory.clamp(1 / approximatedTimeSteps, 0, 1)); - this.callback.set(callback); start(); if (pauseAtStart) { @@ -49,33 +67,20 @@ public Interpolation(double originValue, double targetValue, double approximated } } - public Interpolation(double originValue, double targetValue, double approximatedTimeSteps, @Nullable Runnable callback) { - this(originValue, targetValue, approximatedTimeSteps, false, callback); - } - public Interpolation(double originValue, double targetValue, double approximatedTimeSteps) { - this(originValue, targetValue, approximatedTimeSteps, null); - } - - public Interpolation(double originValue, double targetValue, boolean pauseAtStart, @Nullable Runnable callback) { - this(originValue, targetValue, 35, pauseAtStart, callback); + this(originValue, targetValue, approximatedTimeSteps, false); } public Interpolation(double originValue, double targetValue, boolean pauseAtStart) { - this(originValue, targetValue, 35, pauseAtStart, null); - } - - public Interpolation(double originValue, double targetValue, @Nullable Runnable callback) { - this(originValue, targetValue, false, callback); + this(originValue, targetValue, 35, pauseAtStart); } public Interpolation(double originValue, double targetValue) { - this(originValue, targetValue, false, null); + this(originValue, targetValue, false); } private final AtomicDouble value, targetValue, speed; private final AtomicBoolean started = new AtomicBoolean(false), completed = new AtomicBoolean(false); - private final AtomicReference callback = new AtomicReference<>(null); private final AtomicReference> future = new AtomicReference<>(null); private final Executor executor = Executors.newSingleThreadScheduledExecutor(); @@ -95,10 +100,6 @@ public double approximatedTimeSteps() { return 1 / speed(); } - public Runnable callback() { - return callback.get(); - } - private ScheduledFuture future() { return future.get(); } @@ -115,10 +116,6 @@ public void approximatedTimeSteps(double approximatedTimeSteps) { speed(1 / approximatedTimeSteps); } - public void callback(@Nullable Runnable callback) { - this.callback.set(callback); - } - private void future(@Nullable ScheduledFuture future) { this.future.set(future); } @@ -128,25 +125,25 @@ private void future(@Nullable ScheduledFuture future) { */ @Override public void run() { + Callbacks.StartFrame.EVENT.invoker().onFrameStart(this); + if (!isCompleted() && !started.getAndSet(true)) { - InterpolationCallbacks.Start.EVENT.invoker().onStart(this); + Callbacks.Start.EVENT.invoker().onStart(this); completed.set(false); } else if (isCompleted() && !completed.getAndSet(true)) { - InterpolationCallbacks.Complete.EVENT.invoker().onCompletion(this); + Callbacks.Complete.EVENT.invoker().onCompletion(this); started.set(false); } else { value.accumulateAndGet(targetValue(), (current, target) -> Theory.lerp(current, target, speed())); } - if (callback() != null) { - executor.execute(callback()); - } + Callbacks.EndFrame.EVENT.invoker().onFrameEnd(this); } private void start() { - InterpolationCallbacks.Start.EVENT.invoker().onStart(this); + Callbacks.Start.EVENT.invoker().onStart(this); future(AnimationThreadPoolExecutor.join(this, 0)); }