From ac88049f86d711d1d5c7c503cbeb969c794106d4 Mon Sep 17 00:00:00 2001 From: KrLite <68179735+KrLite@users.noreply.github.com> Date: Sat, 8 Apr 2023 20:50:49 +0800 Subject: [PATCH] revealing 2.2.0 --- .../java/net/krlite/equator/base/Cyclic.java | 29 +++ .../net/krlite/equator/frame/FrameInfo.java | 176 --------------- .../net/krlite/equator/math/geometry/Box.java | 153 ++++++++++--- .../krlite/equator/math/geometry/Vector.java | 2 +- .../krlite/equator/render/BoxRenderer.java | 62 ++---- .../equator/render/GradiantRenderer.java | 52 ++--- .../krlite/equator/render/ModelRenderer.java | 57 ++--- .../equator/render/SectionRenderer.java | 205 ++++++++++++++++++ .../equator/render/base/Renderable.java | 13 ++ .../equator/render/{ => base}/Scissor.java | 11 +- .../equator/render/frame/CursorInfo.java | 17 ++ .../equator/render/frame/FrameInfo.java | 73 +++++++ .../net/krlite/equator/test/CanvasScreen.java | 56 ++--- .../net/krlite/equator/test/LayerScreen.java | 32 +++ .../equator/visual/animation/Animation.java | 26 ++- .../visual/animation/Interpolation.java | 63 ++++-- .../equator/visual/color/AccurateColor.java | 1 - .../krlite/equator/visual/color/Palette.java | 8 + .../krlite/equator/visual/text/Paragraph.java | 130 ++++++----- .../krlite/equator/visual/text/Section.java | 158 ++++++++------ .../equator/visual/texture/Texture.java | 4 +- 21 files changed, 799 insertions(+), 529 deletions(-) create mode 100644 src/main/java/net/krlite/equator/base/Cyclic.java delete mode 100644 src/main/java/net/krlite/equator/frame/FrameInfo.java create mode 100644 src/main/java/net/krlite/equator/render/SectionRenderer.java create mode 100644 src/main/java/net/krlite/equator/render/base/Renderable.java rename src/main/java/net/krlite/equator/render/{ => base}/Scissor.java (57%) create mode 100644 src/main/java/net/krlite/equator/render/frame/CursorInfo.java create mode 100644 src/main/java/net/krlite/equator/render/frame/FrameInfo.java create mode 100644 src/main/java/net/krlite/equator/test/LayerScreen.java diff --git a/src/main/java/net/krlite/equator/base/Cyclic.java b/src/main/java/net/krlite/equator/base/Cyclic.java new file mode 100644 index 0000000..08f5e89 --- /dev/null +++ b/src/main/java/net/krlite/equator/base/Cyclic.java @@ -0,0 +1,29 @@ +package net.krlite.equator.base; + +public interface Cyclic { + T next(); + T previous(); + default T fromBoolean(boolean reverse) { + return reverse ? previous() : next(); + } + + interface Enum> extends Cyclic { + @Override + default T next() { + T[] values = enumValues(); + return values[(ordinal() + 1) % values.length]; + } + + @Override + default T previous() { + T[] values = enumValues(); + return values[(ordinal() - 1 + values.length) % values.length]; + } + + default T[] enumValues() { + return (T[]) getClass().getEnumConstants(); + } + + int ordinal(); + } +} diff --git a/src/main/java/net/krlite/equator/frame/FrameInfo.java b/src/main/java/net/krlite/equator/frame/FrameInfo.java deleted file mode 100644 index 014ab61..0000000 --- a/src/main/java/net/krlite/equator/frame/FrameInfo.java +++ /dev/null @@ -1,176 +0,0 @@ -package net.krlite.equator.frame; - -import net.krlite.equator.math.geometry.Box; -import net.krlite.equator.math.geometry.Vector; -import net.minecraft.client.MinecraftClient; -import org.lwjgl.glfw.GLFW; - -public class FrameInfo { - public static class Scaled { - public static Vector width() { - return Vector.fromCartesian(MinecraftClient.getInstance().getWindow().getScaledWidth(), 0); - } - - public static Vector height() { - return Vector.fromCartesian(0, MinecraftClient.getInstance().getWindow().getScaledHeight()); - } - - public static Vector size() { - return width().add(height()); - } - - public static Vector center() { - return size().divide(2); - } - - public static Vector cursor() { - return Convertor.screenToScaled(Screen.cursor()); - } - - public static Box fullScreen() { - return new Box(size()); - } - - public static class Number { - public static double width() { - return FrameInfo.Scaled.width().magnitude(); - } - - public static double height() { - return FrameInfo.Scaled.height().magnitude(); - } - - public static double size() { - return FrameInfo.Scaled.size().magnitude(); - } - - public static double xCenter() { - return FrameInfo.Scaled.center().x(); - } - - public static double yCenter() { - return FrameInfo.Scaled.center().y(); - } - - public static double xCursor() { - return FrameInfo.Scaled.cursor().x(); - } - - public static double yCursor() { - return FrameInfo.Scaled.cursor().y(); - } - } - } - - public static class Screen { - public static Vector width() { - int[] width = new int[1]; - GLFW.glfwGetWindowSize(MinecraftClient.getInstance().getWindow().getHandle(), width, null); - return Vector.fromCartesian(width[0], 0); - } - - public static Vector height() { - int[] height = new int[1]; - GLFW.glfwGetWindowSize(MinecraftClient.getInstance().getWindow().getHandle(), null, height); - return Vector.fromCartesian(0, height[0]); - } - - public static Vector size() { - return width().add(height()); - } - - public static Vector center() { - return size().divide(2); - } - - public static Vector cursor() { - double[] x = new double[1], y = new double[1]; - GLFW.glfwGetCursorPos(MinecraftClient.getInstance().getWindow().getHandle(), x, y); - return Vector.fromCartesian(x[0], y[0]); - } - - public static Box fullScreen() { - return new Box(size()); - } - - public static class Number { - public static double width() { - return FrameInfo.Screen.width().magnitude(); - } - - public static double height() { - return FrameInfo.Screen.height().magnitude(); - } - - public static double size() { - return FrameInfo.Screen.size().magnitude(); - } - - public static double xCenter() { - return FrameInfo.Screen.center().x(); - } - - public static double yCenter() { - return FrameInfo.Screen.center().y(); - } - - public static double xCursor() { - return FrameInfo.Screen.cursor().x(); - } - - public static double yCursor() { - return FrameInfo.Screen.cursor().y(); - } - } - } - - public static class Convertor { - public static Vector scaledToScreen(Vector vector) { - return vector.multiply(Screen.size().magnitude() / Scaled.size().magnitude()); - } - - public static Vector screenToScaled(Vector vector) { - return vector.multiply(Scaled.size().magnitude() / Screen.size().magnitude()); - } - - public static Vector scaledToOpenGL(Vector vector) { - return screenToOpenGL(scaledToScreen(vector)); - } - - public static Vector openGLToScaled(Vector vector) { - return screenToScaled(openGLToScreen(vector)); - } - - public static Vector screenToOpenGL(Vector vector) { - return vector.y(Screen.height().magnitude() - vector.y()).multiply(2); - } - - public static Vector openGLToScreen(Vector vector) { - return vector.y(Screen.height().magnitude() - vector.y()).divide(2); - } - - public static Box scaledToScreen(Box box) { - return new Box(scaledToScreen(box.origin()), scaledToScreen(box.size())); - } - - public static Box screenToScaled(Box box) { - return new Box(screenToScaled(box.origin()), screenToScaled(box.size())); - } - - public static Box scaledToOpenGL(Box box) { - return screenToOpenGL(scaledToScreen(box)); - } - - public static Box openGLToScaled(Box box) { - return screenToScaled(openGLToScreen(box)); - } - - public static Box screenToOpenGL(Box box) { - return new Box(box.origin().y(Screen.height().magnitude() - box.origin().y() - box.height().magnitude()).multiply(2), box.size().multiply(2)); - } - - public static Box openGLToScreen(Box box) { - return new Box(box.origin().y(Screen.height().magnitude() - box.origin().y() - box.height().magnitude()).divide(2), box.size().divide(2)); - } - } -} diff --git a/src/main/java/net/krlite/equator/math/geometry/Box.java b/src/main/java/net/krlite/equator/math/geometry/Box.java index 8bd39f8..d92f05e 100644 --- a/src/main/java/net/krlite/equator/math/geometry/Box.java +++ b/src/main/java/net/krlite/equator/math/geometry/Box.java @@ -1,11 +1,13 @@ package net.krlite.equator.math.geometry; -import net.krlite.equator.frame.FrameInfo; +import net.krlite.equator.render.frame.FrameInfo; import net.krlite.equator.render.BoxRenderer; import net.krlite.equator.render.GradiantRenderer; import net.krlite.equator.render.ModelRenderer; -import net.krlite.equator.render.Scissor; +import net.krlite.equator.render.SectionRenderer; +import net.krlite.equator.render.base.Scissor; import net.krlite.equator.visual.color.AccurateColor; +import net.krlite.equator.visual.text.Section; import net.krlite.equator.visual.texture.Texture; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -87,6 +89,22 @@ public Vector topRight() { return Vector.fromCartesian(bottomRight().x(), topLeft().y()); } + public Vector topCenter() { + return Vector.fromCartesian(center().x(), top()); + } + + public Vector bottomCenter() { + return Vector.fromCartesian(center().x(), bottom()); + } + + public Vector leftCenter() { + return Vector.fromCartesian(left(), center().y()); + } + + public Vector rightCenter() { + return Vector.fromCartesian(right(), center().y()); + } + public double top() { return topLeft().y(); } @@ -124,7 +142,7 @@ public Box size(Vector size) { } public Box topLeft(Vector topLeft) { - return origin(topLeft); + return Box.fromVector(topLeft, bottomRight()); } public Box bottomLeft(Vector bottomLeft) { @@ -139,6 +157,22 @@ public Box topRight(Vector topRight) { return Box.fromVector(bottomLeft(), topRight); } + public Box topCenter(Vector topCenter) { + return Box.fromVector(topCenter.subtract(width().divide(2)), topCenter.add(width().divide(2))); + } + + public Box bottomCenter(Vector bottomCenter) { + return Box.fromVector(bottomCenter.subtract(width().divide(2)), bottomCenter.add(width().divide(2))); + } + + public Box leftCenter(Vector leftCenter) { + return Box.fromVector(leftCenter.subtract(height().divide(2)), leftCenter.add(height().divide(2))); + } + + public Box rightCenter(Vector rightCenter) { + return Box.fromVector(rightCenter.subtract(height().divide(2)), rightCenter.add(height().divide(2))); + } + public Box top(double y) { return topLeft(topLeft().y(y)); } @@ -184,33 +218,73 @@ public Box translateTopRight(double xTranslation, double yTranslation) { } public Box translateTop(double yTranslation) { - return top(top() + height().magnitude() * yTranslation); + return top(top() + h() * yTranslation); } public Box translateBottom(double yTranslation) { - return bottom(bottom() + height().magnitude() * yTranslation); + return bottom(bottom() + h() * yTranslation); } public Box translateLeft(double xTranslation) { - return left(left() + width().magnitude() * xTranslation); + return left(left() + w() * xTranslation); } public Box translateRight(double xTranslation) { - return right(right() + width().magnitude() * xTranslation); + return right(right() + w() * xTranslation); } public Box translateWidth(double widthTranslation) { - return width(width().magnitude() + width().multiply(widthTranslation).magnitude()); + return width(w() + width().multiply(widthTranslation).magnitude()); } public Box translateHeight(double heightTranslation) { - return height(height().magnitude() + height().multiply(heightTranslation).magnitude()); + return height(h() + height().multiply(heightTranslation).magnitude()); } - public Box translateCenter(double xTranslation, double yTranslation) { + public Box translate(double xTranslation, double yTranslation) { return center(center().add(width().multiply(xTranslation)).add(height().multiply(yTranslation))); } + public Box shiftTopLeft(Vector offset) { + return topLeft(topLeft().add(offset)); + } + + public Box shiftBottomLeft(Vector offset) { + return bottomLeft(bottomLeft().add(offset)); + } + + public Box shiftBottomRight(Vector offset) { + return bottomRight(bottomRight().add(offset)); + } + + public Box shiftTopRight(Vector offset) { + return topRight(topRight().add(offset)); + } + + public Box shiftTop(double y) { + return shiftTopLeft(Vector.fromCartesian(topLeft().x(), y)); + } + + public Box shiftBottom(double y) { + return shiftBottomLeft(Vector.fromCartesian(bottomLeft().x(), y)); + } + + public Box shiftLeft(double x) { + return shiftTopLeft(Vector.fromCartesian(x, topLeft().y())); + } + + public Box shiftRight(double x) { + return shiftTopRight(Vector.fromCartesian(x, topRight().y())); + } + + public Box shift(Vector offset) { + return center(center().add(offset)); + } + + public Box shift(double xOffset, double yOffset) { + return shift(Vector.fromCartesian(xOffset, yOffset)); + } + public Box alignTopLeft(Vector topLeft) { return new Box(topLeft, size()); } @@ -260,27 +334,38 @@ public double area() { return Math.abs(width().cross(height())); } - public Box squareOuter() { - double max = width().magnitudeMax(height()); - return width(max).height(max).center(center()); + public double x() { + return topLeft().x(); } - public Box squareInner() { - double min = width().magnitudeMin(height()); - return width(min).height(min).center(center()); + public double y() { + return topLeft().y(); } - public Box shift(Vector offset) { - return center(center().add(offset)); + public double w() { + return width().magnitude(); } - public Box shift(double xOffset, double yOffset) { - return shift(Vector.fromCartesian(xOffset, yOffset)); + public double h() { + return height().magnitude(); } - public Box translate(double xTranslation, double yTranslation) { - Vector translatedSize = Vector.fromCartesian(size().x() * xTranslation, size().y() * yTranslation); - return topLeft(topLeft().add(translatedSize)); + public double xCenter() { + return center().x(); + } + + public double yCenter() { + return center().y(); + } + + public Box squareOuter() { + double max = width().magnitudeMax(height()); + return width(max).height(max).center(center()); + } + + public Box squareInner() { + double min = width().magnitudeMin(height()); + return width(min).height(min).center(center()); } public Box rotateByRightAngle(int rotationCount) { @@ -295,19 +380,19 @@ public Box rotateByRightAngleCentered(int rotationCount) { } public Box scale(double xScalar, double yScalar) { - return width(width().magnitude() * xScalar).height(height().magnitude() * yScalar); + return width(w() * xScalar).height(h() * yScalar); } public Box scale(double scalar) { return scale(scalar, scalar); } - public Box scaleCentered(double xScalar, double yScalar) { + public Box scaleCenter(double xScalar, double yScalar) { return scale(xScalar, yScalar).center(center()); } - public Box scaleCentered(double scalar) { - return scaleCentered(scalar, scalar); + public Box scaleCenter(double scalar) { + return scaleCenter(scalar, scalar); } public Box expand(Vector expansion) { @@ -335,7 +420,7 @@ public Box expandCentered(double expansion) { } public Box normalizeBy(Box another) { - return scaleCentered(1 / another.width().magnitude(), 1 / another.height().magnitude()) + return scaleCenter(1 / another.w(), 1 / another.height().magnitude()) .center(center().subtract(another.center()).scaleX(1 / another.width().magnitude()).scaleY(1 / another.height().magnitude())); } @@ -343,8 +428,8 @@ public Box interpolate(Vector vector, double lambda) { return new Box(topLeft().interpolate(vector, lambda), size().interpolate(vector, lambda)); } - public Box interpolate(Box box, double lambda) { - return new Box(topLeft().interpolate(box.topLeft(), lambda), size().interpolate(box.topRight(), lambda)); + public Box interpolate(Box another, double lambda) { + return new Box(topLeft().interpolate(another.topLeft(), lambda), size().interpolate(another.size(), lambda)); } public Box fitToScreen() { @@ -352,7 +437,7 @@ public Box fitToScreen() { } public Box fitToOpenGL() { - return FrameInfo.Convertor.screenToOpenGL(this); + return FrameInfo.Convertor.scaledToOpenGL(this); } /** @@ -443,11 +528,15 @@ public ModelRenderer ready(Block block) { return new ModelRenderer(this).model(block); } + public SectionRenderer ready(Section section) { + return new SectionRenderer(this, section); + } + /** * Transform this {@link Box} into a {@link Scissor}. * @return A {@link Scissor} with the same dimensions as this {@link Box}. */ - public Scissor toScissor() { + public Scissor scissor() { return new Scissor(this); } diff --git a/src/main/java/net/krlite/equator/math/geometry/Vector.java b/src/main/java/net/krlite/equator/math/geometry/Vector.java index a21d2f5..78bc2c2 100644 --- a/src/main/java/net/krlite/equator/math/geometry/Vector.java +++ b/src/main/java/net/krlite/equator/math/geometry/Vector.java @@ -1,6 +1,6 @@ package net.krlite.equator.math.geometry; -import net.krlite.equator.frame.FrameInfo; +import net.krlite.equator.render.frame.FrameInfo; import net.krlite.equator.math.algebra.Theory; /** diff --git a/src/main/java/net/krlite/equator/render/BoxRenderer.java b/src/main/java/net/krlite/equator/render/BoxRenderer.java index 904ba74..613e159 100644 --- a/src/main/java/net/krlite/equator/render/BoxRenderer.java +++ b/src/main/java/net/krlite/equator/render/BoxRenderer.java @@ -1,10 +1,10 @@ package net.krlite.equator.render; import com.mojang.blaze3d.systems.RenderSystem; -import net.krlite.equator.frame.FrameInfo; -import net.krlite.equator.math.algebra.Theory; +import net.krlite.equator.render.frame.FrameInfo; import net.krlite.equator.math.geometry.Box; import net.krlite.equator.math.geometry.Vector; +import net.krlite.equator.render.base.Renderable; import net.krlite.equator.visual.color.AccurateColor; import net.krlite.equator.visual.texture.Texture; import net.minecraft.client.render.*; @@ -13,11 +13,12 @@ import org.joml.Matrix4f; import org.joml.Quaterniond; import org.joml.Quaterniondc; +import org.joml.Quaternionf; import java.util.Objects; import java.util.function.UnaryOperator; -public class BoxRenderer { +public record BoxRenderer(Box box, Quaterniondc modifier, @Nullable AccurateColor color, @Nullable Texture texture) implements Renderable { protected enum State { UNABLE(null), COLOR(VertexFormats.POSITION_COLOR), @@ -37,41 +38,17 @@ public VertexFormat vertexFormat() { } } - public BoxRenderer(Box box, Quaterniondc modifier, @Nullable AccurateColor color, @Nullable Texture texture) { - this.box = box; - this.modifier = modifier; - this.color = color; - this.texture = texture; - } - public BoxRenderer(Box box) { this(box, new Quaterniond(), null, null); } - private final Box box; - private final Quaterniondc modifier; - @Nullable - private final AccurateColor color; - @Nullable - private final Texture texture; + // box() is a record method - public Box box() { - return box; - } - - public Quaterniondc modifier() { - return modifier; - } + // modifier() is a record method - @Nullable - public AccurateColor color() { - return color; - } + // color() is a record method - @Nullable - public Texture texture() { - return texture; - } + // texture() is a record method private BoxRenderer preserve(Box box, Box uvBox) { return new BoxRenderer(box, modifier(), color(), hasTexture() ? Objects.requireNonNull(texture()).uvBox(uvBox) : texture()); @@ -98,7 +75,7 @@ public BoxRenderer color(UnaryOperator color) { } public BoxRenderer removeColor() { - return new BoxRenderer(box(), modifier(), null, texture()); + return color(color -> null); } public BoxRenderer texture(Texture texture) { @@ -106,11 +83,12 @@ public BoxRenderer texture(Texture texture) { } public BoxRenderer removeTexture() { - return new BoxRenderer(box(), modifier(), color(), null); + return texture(null); } + @Override public boolean isRenderable() { - return Theory.looseGreater(box().area(), 0) || hasTexture() || hasColor(); + return Renderable.isBoxLegal(box()) && (hasTexture() || hasColor()); } public boolean hasColor() { @@ -159,6 +137,10 @@ private void renderVertex(BufferBuilder builder, Matrix4f matrix, Vector vertex, } public void render(MatrixStack matrixStack, float z) { + if (!isRenderable()) { + return; + } + if (hasColor()) { RenderSystem.enableBlend(); } @@ -174,6 +156,9 @@ public void render(MatrixStack matrixStack, float z) { RenderSystem.setShaderTexture(0, Objects.requireNonNull(texture()).identifier()); } + matrixStack.push(); + matrixStack.multiply(new Quaternionf(modifier())); + BufferBuilder builder = Tessellator.getInstance().getBuffer(); Matrix4f matrix = matrixStack.peek().getPositionMatrix(); @@ -185,6 +170,7 @@ public void render(MatrixStack matrixStack, float z) { renderVertex(builder, matrix, box().topRight(), hasTexture() ? Objects.requireNonNull(texture()).uvTopRight() : Vector.UNIT_X, color(), z); BufferRenderer.drawWithGlobalProgram(builder.end()); + matrixStack.pop(); if (hasColor()) { RenderSystem.disableBlend(); @@ -196,7 +182,7 @@ public void render(MatrixStack matrixStack) { } public void renderFixedCorners(MatrixStack matrixStack, float z) { - Box corner = box().squareInner().scaleCentered(0.5); + Box corner = box().squareInner().scaleCenter(0.5); // Top left preserve(corner.alignTopLeft(box().topLeft()), new Box(0, 0, 0.5, 0.5)).render(matrixStack, z); @@ -210,7 +196,7 @@ public void renderFixedCorners(MatrixStack matrixStack, float z) { // Top right preserve(corner.alignTopRight(box().topRight()), new Box(0.5, 0, 1, 0.5)).render(matrixStack, z); - if (box().width().magnitude() > box().height().magnitude()) { + if (box().w() > box().height().magnitude()) { Box gap = Box.fromVector(corner.alignTopLeft(box().topLeft()).topRight(), corner.alignTopRight(box().topRight()).bottomLeft()); // Top @@ -219,7 +205,7 @@ public void renderFixedCorners(MatrixStack matrixStack, float z) { // Bottom preserve(gap.translate(0, 1), new Box(0.5, 0.5, 0.5, 1)).render(matrixStack, z); } - else if (box().width().magnitude() < box().height().magnitude()) { + else if (box().w() < box().height().magnitude()) { Box gap = Box.fromVector(corner.alignTopLeft(box().topLeft()).bottomLeft(), corner.alignBottomLeft(box().bottomLeft()).topRight()); // Left @@ -235,7 +221,7 @@ public void renderFixedCorners(MatrixStack matrixStack) { } public void renderAndTile(MatrixStack matrixStack, float z) { - preserve(FrameInfo.Scaled.fullScreen(), FrameInfo.Scaled.fullScreen().normalizeBy(box()).shift(0.5, 0.5)).render(matrixStack, z); + preserve(FrameInfo.scaled(), FrameInfo.scaled().normalizeBy(box()).shift(0.5, 0.5)).render(matrixStack, z); } public void renderAndTile(MatrixStack matrixStack) { diff --git a/src/main/java/net/krlite/equator/render/GradiantRenderer.java b/src/main/java/net/krlite/equator/render/GradiantRenderer.java index c5427c5..0b7c414 100644 --- a/src/main/java/net/krlite/equator/render/GradiantRenderer.java +++ b/src/main/java/net/krlite/equator/render/GradiantRenderer.java @@ -1,8 +1,10 @@ package net.krlite.equator.render; import com.mojang.blaze3d.systems.RenderSystem; +import net.krlite.equator.math.algebra.Theory; import net.krlite.equator.math.geometry.Box; import net.krlite.equator.math.geometry.Vector; +import net.krlite.equator.render.base.Renderable; import net.krlite.equator.visual.color.AccurateColor; import net.minecraft.client.render.*; import net.minecraft.client.util.math.MatrixStack; @@ -10,23 +12,15 @@ import java.util.function.UnaryOperator; -public class GradiantRenderer { - public static GradiantRenderer startHorizontal(Box box, AccurateColor left, AccurateColor right) { +public record GradiantRenderer(Box box, AccurateColor topLeft, AccurateColor bottomLeft, AccurateColor bottomRight, AccurateColor topRight) implements Renderable { + public static GradiantRenderer readyHorizontal(Box box, AccurateColor left, AccurateColor right) { return new GradiantRenderer(box, left, left, right, right); } - public static GradiantRenderer startVertical(Box box, AccurateColor top, AccurateColor bottom) { + public static GradiantRenderer readyVertical(Box box, AccurateColor top, AccurateColor bottom) { return new GradiantRenderer(box, top, bottom, bottom, top); } - public GradiantRenderer(Box box, AccurateColor topLeft, AccurateColor bottomLeft, AccurateColor bottomRight, AccurateColor topRight) { - this.box = box; - this.topLeft = topLeft; - this.bottomLeft = bottomLeft; - this.bottomRight = bottomRight; - this.topRight = topRight; - } - public GradiantRenderer(Box box, AccurateColor color) { this(box, color, color, color, color); } @@ -35,28 +29,15 @@ public GradiantRenderer(Box box) { this(box, AccurateColor.TRANSPARENT); } - private final Box box; - private final AccurateColor topLeft, bottomLeft, bottomRight, topRight; + // box() is a record method - public Box box() { - return box; - } - - public AccurateColor topLeft() { - return topLeft; - } + // topLeft() is a record method - public AccurateColor bottomLeft() { - return bottomLeft; - } + // bottomLeft() is a record method - public AccurateColor bottomRight() { - return bottomRight; - } + // bottomRight() is a record method - public AccurateColor topRight() { - return topRight; - } + // topRight() is a record method private GradiantRenderer preserve(Box box) { return new GradiantRenderer(box, topLeft(), bottomLeft(), bottomRight(), topRight()); @@ -183,10 +164,15 @@ public AccurateColor getCenter() { return get(0.5, 0.5); } - private boolean existsColor() { + public boolean existsColor() { return topLeft().hasColor() || topRight().hasColor() || bottomRight().hasColor() || bottomLeft().hasColor(); } + @Override + public boolean isRenderable() { + return Renderable.isBoxLegal(box()) && existsColor(); + } + private AccurateColor[] getColors() { return new AccurateColor[] { topLeft(), bottomLeft(), @@ -234,7 +220,7 @@ private void renderVertex(BufferBuilder builder, Matrix4f matrix, Vector vertex, } public void render(MatrixStack matrixStack, float z) { - if (!existsColor()) { + if (!isRenderable()) { return; } @@ -265,10 +251,10 @@ public enum OutlineMode { public void renderOutline(MatrixStack matrixStack, Vector expansion, OutlineMode outlineMode, float z) { Box corner = Box.fromVectorCentered(box().center(), expansion); - Box gapHorizontal = Box.fromVectorCentered(box().center(), Vector.fromCartesian(box().width().magnitude(), expansion.y())); + Box gapHorizontal = Box.fromVectorCentered(box().center(), Vector.fromCartesian(box().w(), expansion.y())); Box gapVertical = Box.fromVectorCentered(box().center(), Vector.fromCartesian(expansion.x(), box().height().magnitude())); - double width = box().width().magnitude() + expansion.x() * 2, height = box().height().magnitude() + expansion.y() * 2; + double width = box().w() + expansion.x() * 2, height = box().h() + expansion.y() * 2; double xCornerScalar = expansion.x() / width, yCornerScalar = expansion.y() / height; AccurateColor topLeft = topLeft(), topLeftBottom = AccurateColor.TRANSPARENT, topLeftTop = AccurateColor.TRANSPARENT, topLeftDiagonal = AccurateColor.TRANSPARENT; diff --git a/src/main/java/net/krlite/equator/render/ModelRenderer.java b/src/main/java/net/krlite/equator/render/ModelRenderer.java index dfb1775..2fa2be1 100644 --- a/src/main/java/net/krlite/equator/render/ModelRenderer.java +++ b/src/main/java/net/krlite/equator/render/ModelRenderer.java @@ -2,7 +2,9 @@ import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; +import net.krlite.equator.math.algebra.Theory; import net.krlite.equator.math.geometry.Box; +import net.krlite.equator.render.base.Renderable; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; @@ -22,60 +24,28 @@ import java.util.function.UnaryOperator; -public class ModelRenderer { +public record ModelRenderer(Box box, Quaterniondc modifier, @Nullable ItemStack itemStack, @Nullable BlockState blockState, boolean leftHanded) implements Renderable { public ModelRenderer(Box box, Quaterniondc modifier, @Nullable ItemStack itemStack, boolean leftHanded) { - this.box = box; - this.modifier = modifier; - this.itemStack = itemStack; - this.blockState = null; - this.leftHanded = leftHanded; + this(box, modifier, itemStack, null, leftHanded); } public ModelRenderer(Box box, Quaterniondc modifier, @Nullable BlockState blockState, boolean leftHanded) { - this.box = box; - this.modifier = modifier; - this.itemStack = null; - this.blockState = blockState; - this.leftHanded = leftHanded; + this(box, modifier, null, blockState, leftHanded); } public ModelRenderer(Box box) { - this.box = box; - this.modifier = new Quaterniond(); - this.itemStack = null; - this.blockState = null; - this.leftHanded = false; + this(box, new Quaterniond(), null, null, false); } - private final Box box; - private final Quaterniondc modifier; - @Nullable - private final ItemStack itemStack; - @Nullable - private final BlockState blockState; - private final boolean leftHanded; + // box() is a record method - public Box box() { - return box; - } - - public Quaterniondc modifier() { - return modifier; - } + // modifier() is a record method - @Nullable - public ItemStack itemStack() { - return itemStack; - } + // itemStack() is a record method - @Nullable - public BlockState blockState() { - return blockState; - } + // blockState() is a record method - public boolean leftHanded() { - return leftHanded; - } + // leftHanded() is a record method public ModelRenderer modifier(Quaterniondc modifier) { return hasItem() @@ -123,8 +93,9 @@ public boolean hasBlock() { return blockState != null; } + @Override public boolean isRenderable() { - return hasItem() || hasBlock(); + return Renderable.isBoxLegal(box()) && (hasItem() || hasBlock()) && !(hasItem() && hasBlock()); } @SuppressWarnings("deprecation") @@ -139,7 +110,7 @@ private void prepareModel() { private void applyModelView(MatrixStack matrixStack) { matrixStack.scale(1, -1, 1); - matrixStack.scale((float) box().width().magnitude(), (float) box().height().magnitude(), 1); + matrixStack.scale((float) box().w(), (float) box().h(), 1); matrixStack.multiply(new Quaternionf(modifier())); RenderSystem.applyModelViewMatrix(); diff --git a/src/main/java/net/krlite/equator/render/SectionRenderer.java b/src/main/java/net/krlite/equator/render/SectionRenderer.java new file mode 100644 index 0000000..e55e4cd --- /dev/null +++ b/src/main/java/net/krlite/equator/render/SectionRenderer.java @@ -0,0 +1,205 @@ +package net.krlite.equator.render; + +import net.krlite.equator.math.geometry.Box; +import net.krlite.equator.math.geometry.Vector; +import net.krlite.equator.render.base.Renderable; +import net.krlite.equator.render.base.Scissor; +import net.krlite.equator.visual.color.AccurateColor; +import net.krlite.equator.visual.color.Palette; +import net.krlite.equator.visual.text.Paragraph; +import net.krlite.equator.visual.text.Section; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.util.math.MatrixStack; +import org.jetbrains.annotations.Nullable; +import org.joml.Quaterniond; +import org.joml.Quaterniondc; +import org.joml.Quaternionf; + +import java.util.function.UnaryOperator; + +public record SectionRenderer(Box box, Quaterniondc modifier, Section section, @Nullable AccurateColor color, Section.Alignment vertical, Paragraph.Alignment horizontal, boolean shadow) implements Renderable { + public SectionRenderer(Box box, Section section) { + this(box, new Quaterniond(), section, null, Section.Alignment.TOP, Paragraph.Alignment.LEFT, false); + } + + // box() is a record method + + // modifier() is a record method + + // section() is a record method + + // color() is a record method + + // vertical() is a record method + + // horizontal() is a record method + + // shadow() is a record method + + public SectionRenderer modifier(Quaterniondc modifier) { + return new SectionRenderer(box(), modifier, section(), color(), vertical(), horizontal(), shadow()); + } + + public SectionRenderer modifier(UnaryOperator modifier) { + return modifier(modifier.apply(modifier())); + } + + public SectionRenderer removeModifier() { + return modifier(new Quaterniond()); + } + + public SectionRenderer section(Section section) { + return new SectionRenderer(box(), modifier(), section, color(), vertical(), horizontal(), shadow()); + } + + public SectionRenderer section(UnaryOperator
section) { + return section(section.apply(section())); + } + + public SectionRenderer color(AccurateColor color) { + return new SectionRenderer(box(), modifier(), section(), color, vertical(), horizontal(), shadow()); + } + + public SectionRenderer color(UnaryOperator color) { + return color(color.apply(color())); + } + + public SectionRenderer removeColor() { + return color(color -> null); + } + + public SectionRenderer vertical(Section.Alignment vertical) { + return new SectionRenderer(box(), modifier(), section(), color(), vertical, horizontal(), shadow()); + } + + public SectionRenderer cycleVertical(boolean reverse) { + return vertical(vertical().fromBoolean(reverse)); + } + + public SectionRenderer horizontal(Paragraph.Alignment horizontal) { + return new SectionRenderer(box(), modifier(), section(), color(), vertical(), horizontal, shadow()); + } + + public SectionRenderer cycleHorizontal(boolean reverse) { + return horizontal(horizontal().fromBoolean(reverse)); + } + + public SectionRenderer shadow(boolean shadow) { + return new SectionRenderer(box(), modifier(), section(), color(), vertical(), horizontal(), shadow); + } + + public SectionRenderer withShadow() { + return shadow(true); + } + + public SectionRenderer withoutShadow() { + return shadow(false); + } + + private SectionRenderer preserve(Box box) { + return new SectionRenderer(box, modifier(), section(), color(), vertical(), horizontal(), shadow()); + } + + public double actualHeight() { + return section().actualHeight(box().w()); + } + + @Override + public boolean isRenderable() { + return Renderable.isBoxLegal(box()) && !section().isEmpty(); + } + + public void render(MatrixStack matrixStack, TextRenderer textRenderer, boolean cut) { + if (!isRenderable()) { + return; + } + + Scissor scissor = box().scissor(); + + if (cut) { + scissor.snipOn(); + } + + matrixStack.push(); + matrixStack.translate(box().x(), box().y(), 0); + matrixStack.multiply(new Quaternionf(modifier())); + + section().render(box().alignTopLeft(Vector.ZERO), matrixStack, textRenderer, color(), vertical(), horizontal(), shadow()); + + matrixStack.pop(); + + if (cut) { + scissor.snipOff(); + } + } + + public void render(MatrixStack matrixStack, TextRenderer textRenderer) { + render(matrixStack, textRenderer, false); + } + + public void render(MatrixStack matrixStack, boolean cut) { + render(matrixStack, MinecraftClient.getInstance().textRenderer, cut); + } + + public void render(MatrixStack matrixStack) { + render(matrixStack, false); + } + + public void renderTooltip(MatrixStack matrixStack, TextRenderer textRenderer, double bleeding, boolean ignoreVerticalAlignment) { + Box preserved = box().expand(-bleeding); + double actualHeight = preserve(preserved).actualHeight(); + + renderTooltipBackground(matrixStack, !ignoreVerticalAlignment ? box() : box().height(actualHeight + 2 * bleeding)); + preserve(!ignoreVerticalAlignment ? preserved : preserved.height(actualHeight)).render(matrixStack, textRenderer, true); + } + + public void renderTooltip(MatrixStack matrixStack, double bleeding, boolean ignoreVerticalAlignment) { + renderTooltip(matrixStack, MinecraftClient.getInstance().textRenderer, bleeding, ignoreVerticalAlignment); + } + + public void renderTooltip(MatrixStack matrixStack, TextRenderer textRenderer) { + renderTooltip(matrixStack, textRenderer, 3, true); + } + + public void renderTooltip(MatrixStack matrixStack) { + renderTooltip(matrixStack, MinecraftClient.getInstance().textRenderer); + } + + private void renderTooltipBackground(MatrixStack matrixStack, Box box) { + Box bleed = box.expand(-1); + + // Rectangle + bleed.ready(Palette.Minecraft.TOOLTIP_BACKGROUND).render(matrixStack); + + // Background border + bleed.height(1).alignTop(box.top()).ready(Palette.Minecraft.TOOLTIP_BACKGROUND) + .render(matrixStack); + + bleed.height(1).alignBottom(box.bottom()).ready(Palette.Minecraft.TOOLTIP_BACKGROUND) + .render(matrixStack); + + bleed.width(1).alignLeft(box.left()).ready(Palette.Minecraft.TOOLTIP_BACKGROUND) + .render(matrixStack); + + bleed.width(1).alignRight(box.right()).ready(Palette.Minecraft.TOOLTIP_BACKGROUND) + .render(matrixStack); + + // Border + bleed.height(1).alignTop(box.top() + 1).ready(Palette.Minecraft.TOOLTIP_BORDER_LIGHT) + .render(matrixStack); + + bleed.height(1).alignBottom(box.bottom() - 1).ready(Palette.Minecraft.TOOLTIP_BORDER_DARK) + .render(matrixStack); + + bleed.expand(-1).width(1).alignLeft(box.left() + 1).readyGradiant() + .top(Palette.Minecraft.TOOLTIP_BORDER_LIGHT) + .bottom(Palette.Minecraft.TOOLTIP_BORDER_DARK) + .render(matrixStack); + + bleed.expand(-1).width(1).alignRight(box.right() - 1).readyGradiant() + .top(Palette.Minecraft.TOOLTIP_BORDER_DARK) + .bottom(Palette.Minecraft.TOOLTIP_BORDER_LIGHT) + .render(matrixStack); + } +} diff --git a/src/main/java/net/krlite/equator/render/base/Renderable.java b/src/main/java/net/krlite/equator/render/base/Renderable.java new file mode 100644 index 0000000..3156c26 --- /dev/null +++ b/src/main/java/net/krlite/equator/render/base/Renderable.java @@ -0,0 +1,13 @@ +package net.krlite.equator.render.base; + +import net.krlite.equator.math.algebra.Theory; +import net.krlite.equator.math.geometry.Box; +import org.jetbrains.annotations.Nullable; + +public interface Renderable { + boolean isRenderable(); + + static boolean isBoxLegal(@Nullable Box box) { + return box != null && Theory.looseGreater(box.area(), 0); + } +} diff --git a/src/main/java/net/krlite/equator/render/Scissor.java b/src/main/java/net/krlite/equator/render/base/Scissor.java similarity index 57% rename from src/main/java/net/krlite/equator/render/Scissor.java rename to src/main/java/net/krlite/equator/render/base/Scissor.java index fa32fc5..eee2669 100644 --- a/src/main/java/net/krlite/equator/render/Scissor.java +++ b/src/main/java/net/krlite/equator/render/base/Scissor.java @@ -1,7 +1,6 @@ -package net.krlite.equator.render; +package net.krlite.equator.render.base; import com.mojang.blaze3d.systems.RenderSystem; -import net.krlite.equator.frame.FrameInfo; import net.krlite.equator.math.geometry.Box; import java.util.function.UnaryOperator; @@ -17,13 +16,13 @@ public Scissor box(UnaryOperator box) { return new Scissor(box.apply(box())); } - public void cut() { + public void snipOn() { // Need to fit the box into the OpenGL Coordinate System - Box fitted = FrameInfo.Convertor.scaledToOpenGL(box()); - RenderSystem.enableScissor((int) fitted.topLeft().x(), (int) fitted.topLeft().y(), (int) fitted.width().magnitude(), (int) fitted.height().magnitude()); + Box fitted = box().fitToOpenGL(); + RenderSystem.enableScissor((int) fitted.x(), (int) fitted.y(), (int) fitted.w(), (int) fitted.height().magnitude()); } - public void release() { + public void snipOff() { RenderSystem.disableScissor(); } } diff --git a/src/main/java/net/krlite/equator/render/frame/CursorInfo.java b/src/main/java/net/krlite/equator/render/frame/CursorInfo.java new file mode 100644 index 0000000..36cfbac --- /dev/null +++ b/src/main/java/net/krlite/equator/render/frame/CursorInfo.java @@ -0,0 +1,17 @@ +package net.krlite.equator.render.frame; + +import net.krlite.equator.math.geometry.Vector; +import net.minecraft.client.MinecraftClient; +import org.lwjgl.glfw.GLFW; + +public class CursorInfo { + public static Vector scaledCursor() { + return FrameInfo.Convertor.screenToScaled(screenCursor()); + } + + public static Vector screenCursor() { + double[] x = new double[1], y = new double[1]; + GLFW.glfwGetCursorPos(MinecraftClient.getInstance().getWindow().getHandle(), x, y); + return Vector.fromCartesian(x[0], y[0]); + } +} diff --git a/src/main/java/net/krlite/equator/render/frame/FrameInfo.java b/src/main/java/net/krlite/equator/render/frame/FrameInfo.java new file mode 100644 index 0000000..700d346 --- /dev/null +++ b/src/main/java/net/krlite/equator/render/frame/FrameInfo.java @@ -0,0 +1,73 @@ +package net.krlite.equator.render.frame; + +import net.krlite.equator.math.geometry.Box; +import net.krlite.equator.math.geometry.Vector; +import net.minecraft.client.MinecraftClient; +import org.lwjgl.glfw.GLFW; + +public class FrameInfo { + public static Box scaled() { + return new Box(Vector.fromCartesian(MinecraftClient.getInstance().getWindow().getScaledWidth(), MinecraftClient.getInstance().getWindow().getScaledHeight())); + } + + public static Box screen() { + int[] width = new int[1]; + int[] height = new int[1]; + GLFW.glfwGetWindowSize(MinecraftClient.getInstance().getWindow().getHandle(), width, height); + return new Box(Vector.fromCartesian(width[0], height[0])); + } + + public static Box openGL() { + return scaled().fitToOpenGL(); + } + + public static class Convertor { + public static Vector scaledToScreen(Vector vector) { + return vector.multiply(screen().size().magnitude() / scaled().size().magnitude()); + } + + public static Vector screenToScaled(Vector vector) { + return vector.multiply(scaled().size().magnitude() / screen().size().magnitude()); + } + + public static Vector scaledToOpenGL(Vector vector) { + return screenToOpenGL(scaledToScreen(vector)); + } + + public static Vector openGLToScaled(Vector vector) { + return screenToScaled(openGLToScreen(vector)); + } + + public static Vector screenToOpenGL(Vector vector) { + return vector.y(screen().h() - vector.y()).multiply(2); + } + + public static Vector openGLToScreen(Vector vector) { + return vector.y(screen().h() - vector.y()).divide(2); + } + + public static Box scaledToScreen(Box box) { + return new Box(scaledToScreen(box.origin()), scaledToScreen(box.size())); + } + + public static Box screenToScaled(Box box) { + return new Box(screenToScaled(box.origin()), screenToScaled(box.size())); + } + + public static Box scaledToOpenGL(Box box) { + return screenToOpenGL(scaledToScreen(box)); + } + + public static Box openGLToScaled(Box box) { + return screenToScaled(openGLToScreen(box)); + } + + public static Box screenToOpenGL(Box box) { + return new Box(box.origin().y(screen().h() - box.origin().y() - box.height().magnitude()).multiply(2), box.size().multiply(2)); + } + + public static Box openGLToScreen(Box box) { + return new Box(box.origin().y(screen().h() - box.origin().y() - box.height().magnitude()).divide(2), box.size().divide(2)); + } + } +} diff --git a/src/main/java/net/krlite/equator/test/CanvasScreen.java b/src/main/java/net/krlite/equator/test/CanvasScreen.java index 0892192..d58a9ec 100644 --- a/src/main/java/net/krlite/equator/test/CanvasScreen.java +++ b/src/main/java/net/krlite/equator/test/CanvasScreen.java @@ -1,32 +1,42 @@ package net.krlite.equator.test; -import net.krlite.equator.frame.FrameInfo; +import net.krlite.equator.render.frame.FrameInfo; +import net.krlite.equator.input.Keyboard; import net.krlite.equator.input.Mouse; -import net.krlite.equator.math.algebra.Curves; -import net.krlite.equator.math.geometry.Box; import net.krlite.equator.visual.animation.Interpolation; import net.krlite.equator.visual.color.AccurateColor; -import net.krlite.equator.visual.color.Palette; -import net.krlite.equator.visual.text.Paragraph; -import net.krlite.equator.visual.text.Section; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.Text; -import net.minecraft.util.Identifier; public class CanvasScreen extends Screen { public CanvasScreen() { super(Text.of("Canvas")); } - private final Interpolation interpolation = new Interpolation(0, 1, 120); + private final Interpolation interpolation = new Interpolation(0, 1, 0, 120); { - Interpolation.Callbacks.Complete.EVENT.register(i -> i.targetValue(1 - i.targetValue())); + Mouse.Callbacks.Click.EVENT.register((button, action, mods) -> { + if (MinecraftClient.getInstance().currentScreen != this) return; - Mouse.Callbacks.Click.EVENT.register((button, event, mods) -> { - if (event == Mouse.Action.PRESS) - interpolation.switchPauseResume(); + if (action.isPress()) { + interpolation.reset(); + System.out.println("Mouse click: " + button); + } + }); + + Keyboard.Callbacks.Key.EVENT.register((key, scanCode, action, mods) -> { + if (MinecraftClient.getInstance().currentScreen != this) return; + + if (action.isPress()) { + if (key == Keyboard.E) { + if (client != null) { + client.setScreen(new LayerScreen()); + } + } + } }); } @@ -38,26 +48,6 @@ protected void init() { public void render(MatrixStack matrixStack, int mouseX, int mouseY, float delta) { renderBackground(matrixStack); - //System.out.println("Interpolation value: " + interpolation.value() + ", " + interpolation.targetValue()); - //FrameInfo.Scaled.fullScreen().scale(interpolation.value()).ready(AccurateColor.CYAN).render(matrixStack); - - Box box = FrameInfo.Scaled.fullScreen().translateRight(-0.9 * interpolation.value()).translateTop(0.5 * interpolation.value()); - - box.ready(AccurateColor.BLACK).render(matrixStack); - /* - box.translateTop(0.625).ready(AccurateColor.DARK_GRAY).render(matrixStack); - box.translateTop(0.75).ready(AccurateColor.GRAY).render(matrixStack); - - Paragraph.title(Text.of("Hello, world!")).render(box.translateTop(0.5), textRenderer, matrixStack, AccurateColor.GRAY, false); - Paragraph.subtitle(Text.of("Subtitle")).render(box.translateTop(0.625), textRenderer, matrixStack, AccurateColor.LIGHT_GRAY, false); - Paragraph.of(Text.of("Paragraph with wrapped text. Powered by word-wrap!")).render(box.translateTop(0.75), textRenderer, matrixStack, AccurateColor.WHITE, false); - - */ - - new Section(Text.of("Hello, world!"), 1 + 2.3 * (1 - interpolation.value())).fontSize(1 - 0.6 * interpolation.value()) - .append(Text.of("§oP§4a§5ragraph A - write something here.").copy().styled(style -> style.withBold(true))) - .append(Text.of("Paragraph B - another paragraph.")) - .appendSubtitle(Text.of("Subtitle Without Spacing at Top"), false) - .render(box, textRenderer, matrixStack, Palette.rainbow(Curves.SINE.apply(0.5, 1, System.currentTimeMillis() / 1000.0)), false); + FrameInfo.scaled().ready(AccurateColor.YELLOW.opacity(interpolation.value())).render(matrixStack); } } diff --git a/src/main/java/net/krlite/equator/test/LayerScreen.java b/src/main/java/net/krlite/equator/test/LayerScreen.java new file mode 100644 index 0000000..590214f --- /dev/null +++ b/src/main/java/net/krlite/equator/test/LayerScreen.java @@ -0,0 +1,32 @@ +package net.krlite.equator.test; + +import net.krlite.equator.render.frame.FrameInfo; +import net.krlite.equator.visual.color.AccurateColor; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.Text; +import org.jetbrains.annotations.Nullable; + +public class LayerScreen extends Screen { + protected LayerScreen() { + super(Text.of("Layer")); + } + + @Nullable + private final Screen parent = MinecraftClient.getInstance().currentScreen; + + @Override + public void render(MatrixStack matrixStack, int mouseX, int mouseY, float delta) { + if (parent != null) { + parent.render(matrixStack, mouseX, mouseY, delta); + } + + renderBackground(matrixStack); + } + + @Override + public void renderBackground(MatrixStack matrixStack) { + FrameInfo.scaled().scaleCenter(0.5).ready(AccurateColor.WHITE.opacity(0.3)).render(matrixStack); + } +} 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 ba55f2d..fe209b9 100644 --- a/src/main/java/net/krlite/equator/visual/animation/Animation.java +++ b/src/main/java/net/krlite/equator/visual/animation/Animation.java @@ -4,8 +4,6 @@ import net.fabricmc.fabric.api.event.EventFactory; import org.jetbrains.annotations.Nullable; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -84,6 +82,17 @@ public Animation(double startValue, double endValue, long duration, Slice slice) this(startValue, endValue, duration, TimeUnit.MILLISECONDS, slice); } + protected Animation(Animation parent) { + this.startValue = parent.startValue(); + this.endValue = parent.endValue(); + this.duration = parent.duration(); + this.progress = new AtomicLong(parent.progress.get()); + this.timeUnit = parent.timeUnit(); + this.slice = new AtomicReference<>(parent.slice.get()); + this.repeat = new AtomicBoolean(parent.repeat.get()); + this.future.set(parent.future.get()); + } + private final double startValue, endValue; private final long duration; private final AtomicLong progress; @@ -91,7 +100,10 @@ public Animation(double startValue, double endValue, long duration, Slice slice) private final AtomicReference slice; private final AtomicBoolean repeat; private final AtomicReference> future = new AtomicReference<>(null); - private final Executor executor = Executors.newSingleThreadScheduledExecutor(); + + public Animation copy() { + return new Animation(this); + } public double startValue() { return startValue; @@ -109,16 +121,16 @@ public TimeUnit timeUnit() { return timeUnit; } - public double percentage() { + public double progress() { return progress.get() / (double) duration; } public double value() { - return slice().apply(startValue(), endValue(), percentage()); + return slice().apply(startValue(), endValue(), progress()); } - public double valueAsPercentage() { - return slice().apply(0, 1, percentage()); + public double percentage() { + return slice().apply(0, 1, progress()); } public Slice slice() { 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 5d736e0..362c4d2 100644 --- a/src/main/java/net/krlite/equator/visual/animation/Interpolation.java +++ b/src/main/java/net/krlite/equator/visual/animation/Interpolation.java @@ -6,8 +6,6 @@ import net.krlite.equator.math.algebra.Theory; import org.jetbrains.annotations.Nullable; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -55,16 +53,21 @@ interface EndFrame { } } - public Interpolation(double originValue, double targetValue, double approximatedDuration, boolean pauseAtStart) { + public Interpolation(double value, double originValue, double targetValue, double approximatedDuration, boolean pauseAtStart) { + this.value = new AtomicDouble(value); + this.originValue = new AtomicDouble(originValue); this.targetValue = new AtomicDouble(targetValue); - this.value = new AtomicDouble(originValue); this.speed = new AtomicDouble(Theory.clamp(1 / approximatedDuration, 0, 1)); - start(); - if (pauseAtStart) { - pause(); - reset(originValue); - } + start(pauseAtStart); + } + + public Interpolation(double value, double originValue, double targetValue, double approximatedDuration) { + this(value, originValue, targetValue, approximatedDuration, false); + } + + public Interpolation(double originValue, double targetValue, double approximatedDuration, boolean pauseAtStart) { + this(originValue, originValue, targetValue, approximatedDuration, pauseAtStart); } public Interpolation(double originValue, double targetValue, double approximatedDuration) { @@ -78,20 +81,41 @@ public Interpolation(double originValue, double targetValue, boolean pauseAtStar public Interpolation(double originValue, double targetValue) { this(originValue, targetValue, false); } + + protected Interpolation(Interpolation parent) { + this.value = new AtomicDouble(parent.value.get()); + this.originValue = new AtomicDouble(parent.originValue.get()); + this.targetValue = new AtomicDouble(parent.targetValue.get()); + this.speed = new AtomicDouble(parent.speed.get()); + this.started.set(parent.started.get()); + this.completed.set(parent.completed.get()); + this.future.set(parent.future.get()); + } - private final AtomicDouble value, targetValue, speed; + private final AtomicDouble value, originValue, targetValue, speed; private final AtomicBoolean started = new AtomicBoolean(false), completed = new AtomicBoolean(false); private final AtomicReference> future = new AtomicReference<>(null); - private final Executor executor = Executors.newSingleThreadScheduledExecutor(); - public double targetValue() { - return targetValue.get(); + public Interpolation copy() { + return new Interpolation(this); } public double value() { return value.get(); } + public double percentage() { + return Theory.clamp((value() - originValue()) / (targetValue() - originValue()), 0, 1); + } + + public double originValue() { + return originValue.get(); + } + + public double targetValue() { + return targetValue.get(); + } + public double speed() { return speed.get(); } @@ -104,6 +128,10 @@ private ScheduledFuture future() { return future.get(); } + public void originValue(double originValue) { + this.originValue.set(originValue); + } + public void targetValue(double targetValue) { this.targetValue.set(targetValue); } @@ -142,9 +170,12 @@ else if (isCompleted() && !completed.getAndSet(true)) { Callbacks.EndFrame.EVENT.invoker().onFrameEnd(this); } - private void start() { + private void start(boolean pauseAtStart) { Callbacks.Start.EVENT.invoker().onStart(this); future(AnimationThreadPoolExecutor.join(this, 0)); + if (pauseAtStart) { + future().cancel(true); + } } public void pause() { @@ -169,8 +200,8 @@ public void switchPauseResume() { } } - public void reset(double originValue) { - value.set(originValue); + public void reset() { + value.set(originValue()); } public boolean isRunning() { diff --git a/src/main/java/net/krlite/equator/visual/color/AccurateColor.java b/src/main/java/net/krlite/equator/visual/color/AccurateColor.java index bfc906e..1620069 100644 --- a/src/main/java/net/krlite/equator/visual/color/AccurateColor.java +++ b/src/main/java/net/krlite/equator/visual/color/AccurateColor.java @@ -2,7 +2,6 @@ import com.scrtwpns.Mixbox; import net.krlite.equator.math.algebra.Theory; -import org.jetbrains.annotations.NotNull; import java.awt.*; diff --git a/src/main/java/net/krlite/equator/visual/color/Palette.java b/src/main/java/net/krlite/equator/visual/color/Palette.java index 12bd494..d7ee533 100644 --- a/src/main/java/net/krlite/equator/visual/color/Palette.java +++ b/src/main/java/net/krlite/equator/visual/color/Palette.java @@ -110,5 +110,13 @@ public static class Minecraft { BACKGROUND_GREEN = AccurateColor.fromInt(0x153F15), BACKGROUND_AQUA = AccurateColor.fromInt(0x153F3F), BACKGROUND_RED = AccurateColor.fromInt(0x3F1515), BACKGROUND_LIGHT_PURPLE = AccurateColor.fromInt(0x3F153F), BACKGROUND_YELLOW = AccurateColor.fromInt(0x3F3F15), BACKGROUND_WHITE = AccurateColor.fromInt(0x3F3F3F); + + /** + * Tooltip + *
+ * Colors used in the tooltip. + */ + public static final AccurateColor TOOLTIP_BORDER_LIGHT = AccurateColor.fromInt(0x505000FF), TOOLTIP_BORDER_DARK = AccurateColor.fromInt(0x5028007F), + TOOLTIP_BACKGROUND = AccurateColor.fromInt(0xF0100010); } } diff --git a/src/main/java/net/krlite/equator/visual/text/Paragraph.java b/src/main/java/net/krlite/equator/visual/text/Paragraph.java index b4f7616..79333ef 100644 --- a/src/main/java/net/krlite/equator/visual/text/Paragraph.java +++ b/src/main/java/net/krlite/equator/visual/text/Paragraph.java @@ -1,6 +1,8 @@ package net.krlite.equator.visual.text; +import net.krlite.equator.base.Cyclic; import net.krlite.equator.math.geometry.Box; +import net.krlite.equator.math.geometry.Vector; import net.krlite.equator.visual.color.AccurateColor; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; @@ -9,100 +11,87 @@ import org.davidmoten.text.utils.WordWrap; import java.util.Arrays; +import java.util.LinkedList; import java.util.regex.Matcher; import java.util.regex.Pattern; -public record Paragraph(Text text, double fontSize, double lineSpacing) { +public record Paragraph(Text text, double scalar) { + interface AlignmentFunction { + Vector apply(Box box, Text text, TextRenderer textRenderer, double fontSize, double scalar); + } + + public enum Alignment implements AlignmentFunction, Cyclic.Enum { + LEFT, CENTER, RIGHT; + + AlignmentFunction function() { + return switch (this) { + case LEFT -> (box, text, textRenderer, fontSize, scalar) -> box.topLeft(); + case CENTER -> (box, text, textRenderer, fontSize, scalar) -> box.topCenter().add(-fontSize * scalar * textRenderer.getWidth(text) / 2.0, 0); + case RIGHT -> (box, text, textRenderer, fontSize, scalar) -> box.topRight().add(-fontSize * scalar * textRenderer.getWidth(text), 0); + }; + } + + @Override + public Vector apply(Box box, Text text, TextRenderer textRenderer, double fontSize, double scalar) { + return function().apply(box, text, textRenderer, fontSize, scalar); + } + } + public static final Pattern NEWLINE_PATTERN = Pattern.compile("\\r?\\n"), FORMATTING_PATTERN = Pattern.compile("§(?[0-9a-fk-or])"); public static final String NEWLINE = "\n"; - public static Paragraph of(Text text, double fontSize, double lineSpacing) { - return new Paragraph(text, fontSize, lineSpacing); + public static Paragraph of(Text text, double scalar) { + return new Paragraph(text, scalar); } public static Paragraph of(Text text) { - return new Paragraph(text, Section.DEFAULT_FONT_SIZE, Section.DEFAULT_LINE_SPACING); - } - - public static Paragraph title(Text text, double fontSize, double lineSpacing, double titleScaling) { - return new Paragraph(text, fontSize * titleScaling, lineSpacing); + return new Paragraph(text, 1); } public static Paragraph title(Text text) { - return title(text, Section.DEFAULT_FONT_SIZE, Section.DEFAULT_LINE_SPACING, Section.DEFAULT_TITLE_SCALING); + return of(text, Section.DEFAULT_TITLE_SCALAR); } - - public static Paragraph subtitle(Text text, double fontSize, double lineSpacing, double subtitleScaling) { - return new Paragraph(text, fontSize * subtitleScaling, lineSpacing); - } - public static Paragraph subtitle(Text text) { - return subtitle(text, Section.DEFAULT_FONT_SIZE, Section.DEFAULT_LINE_SPACING, Section.DEFAULT_SUBTITLE_SCALING); + return of(text, Section.DEFAULT_SUBTITLE_SCALAR); } - public static Paragraph spacing(double fontSize, double lineSpacing, double paragraphSpacing) { - return new Paragraph(Text.of(""), fontSize * paragraphSpacing, lineSpacing); - } - - public static Paragraph spacing(double paragraphSpacing) { - return spacing(Section.DEFAULT_FONT_SIZE, Section.DEFAULT_LINE_SPACING, paragraphSpacing); + public static Paragraph spacing(double scalar) { + return new Paragraph(Text.of(""), scalar); } public static Paragraph spacing() { - return spacing(Section.DEFAULT_PARAGRAPH_SPACING); + return spacing(1); } public Paragraph(Text text) { - this(text, Section.DEFAULT_FONT_SIZE, Section.DEFAULT_LINE_SPACING); + this(text, 1); } // text() is a record method - // fontSize() is a record method - - // lineSpacing() is a record method + // scalar() is a record method public Paragraph text(Text text) { - return new Paragraph(text, fontSize(), lineSpacing()); - } - - public Paragraph fontSize(double fontSize) { - return new Paragraph(text(), fontSize, lineSpacing()); + return new Paragraph(text, scalar()); } - public Paragraph lineSpacing(double lineSpacing) { - return new Paragraph(text(), fontSize(), lineSpacing); + public Paragraph scalar(double scalar) { + return new Paragraph(text(), scalar); } public boolean isSpacing() { return text().getString().isEmpty(); } - public double height(boolean withSpacing) { - return fontSize() * MinecraftClient.getInstance().textRenderer.fontHeight * (withSpacing ? 1 + lineSpacing : 1); + public double height(double fontSize, double lineSpacing) { + return fontSize * scalar() * MinecraftClient.getInstance().textRenderer.fontHeight * (isSpacing() ? 1 : (1 + lineSpacing)); } - public double height() { - return height(true); + public double actualHeight(double fontSize, double lineSpacing, double width) { + return height(fontSize, lineSpacing) * countLines(fontSize, width); } - public double heightAuto() { - return height(!isSpacing()); - } - - public double totalHeight(double width, boolean withSpacing) { - return height(withSpacing) * countLines(width); - } - - public double totalHeight(double width) { - return totalHeight(width, true); - } - - public double totalHeightAuto(double width) { - return totalHeight(width, !isSpacing()); - } - - public Text[] wrap(double width) { + public Text[] wrap(double fontSize, double width) { return isSpacing() ? new Text[]{text()} : Arrays.stream(concatFormatting(NEWLINE_PATTERN.matcher( WordWrap.from(text().getString()) .breakWords(true) @@ -111,9 +100,9 @@ public Text[] wrap(double width) { .newLine(NEWLINE) .includeExtraWordChars("0123456789") .includeExtraWordChars("§") - .stringWidth(charSequence -> fontSize() * MinecraftClient.getInstance().textRenderer.getWidth( - Text.literal(FORMATTING_PATTERN.matcher(charSequence).replaceAll("")).setStyle(text().getStyle()))) - .wrap() + .stringWidth(charSequence -> fontSize * scalar() * MinecraftClient.getInstance().textRenderer.getWidth( + Text.literal(FORMATTING_PATTERN.matcher(charSequence).replaceAll("")).setStyle(text().getStyle())) + ).wrap() ).replaceAll(NEWLINE).split(NEWLINE))).map(s -> Text.literal(s).setStyle(text().getStyle())).toArray(Text[]::new); } @@ -133,27 +122,30 @@ private String[] concatFormatting(String[] lines) { return result; } - public int countLines(double width) { - return wrap(width).length; + public int countLines(double fontSize, double width) { + return width <= 0 ? 0 : wrap(fontSize, width).length; } - public void render(Box box, TextRenderer textRenderer, MatrixStack matrixStack, AccurateColor color, boolean shadow) { - render(wrap(box.width().magnitude()), box, textRenderer, matrixStack, color, shadow); + public void render(double fontSize, double lineSpacing, Box box, MatrixStack matrixStack, TextRenderer textRenderer, AccurateColor color, Alignment alignment, boolean shadow) { + if (box.w() <= 0) return; + render(new LinkedList<>(Arrays.stream(wrap(fontSize, box.w())).toList()), fontSize, lineSpacing, box, matrixStack, textRenderer, color, alignment, shadow); } - private void render(Text[] lines, Box box, TextRenderer textRenderer, MatrixStack matrixStack, AccurateColor color, boolean shadow) { - Text line = lines[0]; + private void render(LinkedList lines, double fontSize, double lineSpacing, Box box, MatrixStack matrixStack, TextRenderer textRenderer, AccurateColor color, Alignment alignment, boolean shadow) { + Text line = lines.poll(); + + if (line == null) return; - if (lines.length > 1) { - Text[] copied = Arrays.copyOfRange(lines, 1, lines.length); - copied[0] = copied[0].copy().styled(style -> style.withParent(line.getStyle())); - render(copied, box.shift(0, heightAuto()), textRenderer, matrixStack, color, shadow); + if (lines.peek() != null) { + lines.set(0, lines.peek().copy().styled(style -> style.withParent(line.getStyle()))); + render(lines, fontSize, lineSpacing, box.shift(0, height(fontSize, lineSpacing)), matrixStack, textRenderer, color, alignment, shadow); } matrixStack.push(); - matrixStack.translate(box.topLeft().x(), box.topLeft().y(), 0); - matrixStack.scale((float) fontSize(), (float) fontSize(), 1); + Vector aligned = alignment.apply(box, line, textRenderer, fontSize, scalar()); + matrixStack.translate(aligned.x(), aligned.y(), 0); + matrixStack.scale((float) (fontSize * scalar()), (float) (fontSize * scalar()), 1); if (shadow) { textRenderer.drawWithShadow(matrixStack, line, 0, 0, color.toInt()); diff --git a/src/main/java/net/krlite/equator/visual/text/Section.java b/src/main/java/net/krlite/equator/visual/text/Section.java index 3e83cb4..f731ab0 100644 --- a/src/main/java/net/krlite/equator/visual/text/Section.java +++ b/src/main/java/net/krlite/equator/visual/text/Section.java @@ -1,5 +1,6 @@ package net.krlite.equator.visual.text; +import net.krlite.equator.base.Cyclic; import net.krlite.equator.math.geometry.Box; import net.krlite.equator.visual.color.AccurateColor; import net.minecraft.client.font.TextRenderer; @@ -7,81 +8,76 @@ import net.minecraft.text.Text; import java.util.Arrays; +import java.util.LinkedList; import java.util.stream.Stream; -public class Section { - public Section(double fontSize, double titleScaling, double subtitleScaling, double lineSpacing, double paragraphSpacing, Paragraph... paragraphs) { - this.fontSize = fontSize; - this.titleScaling = titleScaling; - this.subtitleScaling = subtitleScaling; - this.lineSpacing = lineSpacing; - this.paragraphSpacing = paragraphSpacing; - this.paragraphs = paragraphs; +public record Section(double fontSize, double titleScalar, double subtitleScalar, double lineSpacing, double paragraphSpacing, Paragraph... paragraphs) { + interface AlignmentFunction { + Box apply(Box box, double height); } - public Section(Text title, double fontSize, double titleScaling, double subtitleScaling, double lineSpacing, double paragraphSpacing) { - this(fontSize, titleScaling, subtitleScaling, lineSpacing, paragraphSpacing, Paragraph.title(title, fontSize, lineSpacing, titleScaling)); - } + public enum Alignment implements AlignmentFunction, Cyclic.Enum { + TOP, CENTER, BOTTOM; - public Section(Text title, double fontSize) { - this(title, fontSize, DEFAULT_TITLE_SCALING, DEFAULT_SUBTITLE_SCALING, DEFAULT_LINE_SPACING, DEFAULT_PARAGRAPH_SPACING); - } + AlignmentFunction function() { + return switch (this) { + case TOP -> (box, height) -> box; + case CENTER -> (box, height) -> box.top(box.yCenter()).shift(0, -height / 2); + case BOTTOM -> (box, height) -> box.top(box.bottom()).shift(0, -height); + }; + } - public Section(Text title) { - this(title, DEFAULT_FONT_SIZE); + @Override + public Box apply(Box box, double height) { + return function().apply(box, height); + } } - Section(Section section, Paragraph... paragraphs) { - this(section.fontSize(), section.titleScaling(), section.subtitleScaling(), section.lineSpacing(), section.paragraphSpacing(), - section.isEmpty() ? paragraphs : Stream.concat(Stream.of(section.paragraphs()), Stream.of(paragraphs)).toArray(Paragraph[]::new)); + public Section(double fontSize, double titleScalar, double subtitleScalar, double lineSpacing, double paragraphSpacing) { + this(fontSize, titleScalar, subtitleScalar, lineSpacing, paragraphSpacing, new Paragraph[0]); } - public static final double DEFAULT_FONT_SIZE = 1, DEFAULT_TITLE_SCALING = 1.5, DEFAULT_SUBTITLE_SCALING = 1.2, DEFAULT_LINE_SPACING = 1.0 / 10.0, DEFAULT_PARAGRAPH_SPACING = 1; - private final double fontSize, titleScaling, subtitleScaling, lineSpacing, paragraphSpacing; - private final Paragraph[] paragraphs; - - public double fontSize() { - return fontSize; + public Section(double fontSize, double titleScalar, double subtitleScalar) { + this(fontSize, titleScalar, subtitleScalar, DEFAULT_LINE_SPACING, DEFAULT_PARAGRAPH_SPACING); } - public double titleScaling() { - return titleScaling; + public Section(double fontSize) { + this(fontSize, DEFAULT_TITLE_SCALAR, DEFAULT_SUBTITLE_SCALAR); } - public double subtitleScaling() { - return subtitleScaling; + public Section() { + this(DEFAULT_FONT_SIZE); } - public double lineSpacing() { - return lineSpacing; + Section(Section section, Paragraph... paragraphs) { + this(section.fontSize(), section.titleScalar(), section.subtitleScalar(), section.lineSpacing(), section.paragraphSpacing(), + section.isEmpty() ? paragraphs : Stream.concat(Stream.of(section.paragraphs()), Stream.of(paragraphs)).toArray(Paragraph[]::new)); } - public double paragraphSpacing() { - return paragraphSpacing; - } + public static final double DEFAULT_FONT_SIZE = 1, DEFAULT_TITLE_SCALAR = 1.5, DEFAULT_SUBTITLE_SCALAR = 1.2, DEFAULT_LINE_SPACING = 1.0 / 10.0, DEFAULT_PARAGRAPH_SPACING = 1; - public Paragraph[] paragraphs() { - return paragraphs; - } + // fontSize() is a record method - public Section fontSize(double fontSize) { - return new Section(fontSize, titleScaling(), subtitleScaling(), lineSpacing(), paragraphSpacing(), paragraphs()); - } + // titleScalar() is a record method - public Section titleScaling(double titleScaling) { - return new Section(fontSize(), titleScaling, subtitleScaling(), lineSpacing(), paragraphSpacing(), paragraphs()); - } + // subtitleScalar() is a record method - public Section subtitleScaling(double subtitleScaling) { - return new Section(fontSize(), titleScaling(), subtitleScaling, lineSpacing(), paragraphSpacing(), paragraphs()); + // lineSpacing() is a record method + + // paragraphSpacing() is a record method + + // paragraphs() is a record method + + public Section fontSize(double fontSize) { + return new Section(fontSize, titleScalar(), subtitleScalar(), lineSpacing(), paragraphSpacing(), paragraphs()); } public Section lineSpacing(double lineSpacing) { - return new Section(fontSize(), titleScaling(), subtitleScaling(), lineSpacing, paragraphSpacing(), paragraphs()); + return new Section(fontSize(), titleScalar(), subtitleScalar(), lineSpacing, paragraphSpacing(), paragraphs()); } public Section paragraphSpacing(double paragraphSpacing) { - return new Section(fontSize(), titleScaling(), subtitleScaling(), lineSpacing(), paragraphSpacing, paragraphs()); + return new Section(fontSize(), titleScalar(), subtitleScalar(), lineSpacing(), paragraphSpacing, paragraphs()); } public boolean isEmpty() { @@ -89,28 +85,28 @@ public boolean isEmpty() { } public double height() { - return isEmpty() ? 0 : Arrays.stream(paragraphs()).mapToDouble(Paragraph::heightAuto).sum(); + return isEmpty() ? 0 : Arrays.stream(paragraphs()).mapToDouble(paragraph -> paragraph.height(fontSize(), lineSpacing())).sum(); } - public double height(double width) { - return isEmpty() ? 0 : Arrays.stream(paragraphs()).mapToDouble(paragraph -> paragraph.totalHeightAuto(width)).sum(); + public double actualHeight(double width) { + return isEmpty() ? 0 : Arrays.stream(paragraphs()).mapToDouble(paragraph -> paragraph.actualHeight(fontSize(), lineSpacing(), width)).sum(); + } + + private Section appendParagraphRaw(Paragraph paragraph) { + return new Section(this, paragraph); } public Section appendSpacing(double spacing) { - return appendParagraphRaw(Paragraph.spacing(fontSize(), lineSpacing(), paragraphSpacing() * spacing)); + return appendParagraphRaw(Paragraph.spacing(spacing)); } public Section appendSpacing() { return appendSpacing(1); } - private Section appendParagraphRaw(Paragraph paragraph) { - return new Section(this, paragraph); - } - private Section appendParagraph(Paragraph paragraph, double spacing) { return Arrays.stream(paragraphs()).skip(Math.max(0, paragraphs().length - 1)).findFirst().map(lastParagraph -> { - if (paragraphs().length == 0 || paragraph.isSpacing() || lastParagraph.isSpacing()) { + if (paragraph.isSpacing() || lastParagraph.isSpacing()) { return appendParagraphRaw(paragraph); } else { @@ -131,42 +127,60 @@ public Section append(Paragraph paragraph) { return append(paragraph, true); } + public Section append(Text text, double scalar, boolean hasSpacing) { + return append(Paragraph.of(text, scalar), hasSpacing); + } + public Section append(Text text, boolean hasSpacing) { - return append(Paragraph.of(text, fontSize(), lineSpacing()), hasSpacing); + return append(text, 1, hasSpacing); + } + + public Section append(Text text, double scalar) { + return append(text, scalar, true); } public Section append(Text text) { - return append(text, true); + return append(text, 1); + } + + public Section appendTitle(Text text, boolean bold, boolean hasSpacing) { + return append(Paragraph.of(text.copy().styled(style -> style.withBold(bold)), titleScalar()), hasSpacing); } - public Section appendTitle(Text text, boolean hasSpacing) { - return append(Paragraph.title(text, fontSize(), lineSpacing(), titleScaling()), hasSpacing); + public Section appendTitle(Text text, boolean bold) { + return appendTitle(text, bold, true); } public Section appendTitle(Text text) { - return appendTitle(text, true); + return appendTitle(text, false); + } + + public Section appendSubtitle(Text text, boolean bold, boolean hasSpacing) { + return append(Paragraph.of(text.copy().styled(style -> style.withBold(bold)), subtitleScalar()), hasSpacing); } - public Section appendSubtitle(Text text, boolean hasSpacing) { - return append(Paragraph.subtitle(text, fontSize(), lineSpacing(), subtitleScaling()), hasSpacing); + public Section appendSubtitle(Text text, boolean bold) { + return appendSubtitle(text, bold, true); } public Section appendSubtitle(Text text) { - return appendSubtitle(text, true); + return appendTitle(text, false); } - public void render(Box box, TextRenderer textRenderer, MatrixStack matrixStack, AccurateColor color, boolean shadow) { - render(paragraphs(), box, textRenderer, matrixStack, color, shadow); + public void render(Box box, MatrixStack matrixStack, TextRenderer textRenderer, AccurateColor color, Alignment vertical, Paragraph.Alignment horizontal, boolean shadow) { + render(new LinkedList<>(Arrays.stream(paragraphs()).toList()), vertical.apply(box, actualHeight(box.w())), matrixStack, textRenderer, color, vertical, horizontal, shadow); } - private void render(Paragraph[] paragraphs, Box box, TextRenderer textRenderer, MatrixStack matrixStack, AccurateColor color, boolean shadow) { - Paragraph paragraph = paragraphs[0]; + private void render(LinkedList paragraphs, Box box, MatrixStack matrixStack, TextRenderer textRenderer, AccurateColor color, Alignment vertical, Paragraph.Alignment horizontal, boolean shadow) { + Paragraph paragraph = paragraphs.poll(); + + if (paragraph == null) return; - if (paragraphs.length > 1) { - render(Arrays.copyOfRange(paragraphs, 1, paragraphs.length), box.shift(0, paragraph.isSpacing() ? paragraph.height() : paragraph.totalHeightAuto(box.width().magnitude())), - textRenderer, matrixStack, color, shadow); + if (paragraphs.peek() != null) { + render(paragraphs, box.shift(0, paragraph.actualHeight(fontSize(), lineSpacing(), box.w())), + matrixStack, textRenderer, color, vertical, horizontal, shadow); } - paragraph.render(box, textRenderer, matrixStack, color, shadow); + paragraph.render(fontSize(), lineSpacing(), box, matrixStack, textRenderer, color, horizontal, shadow); } } diff --git a/src/main/java/net/krlite/equator/visual/texture/Texture.java b/src/main/java/net/krlite/equator/visual/texture/Texture.java index b36e0e6..379a4f7 100644 --- a/src/main/java/net/krlite/equator/visual/texture/Texture.java +++ b/src/main/java/net/krlite/equator/visual/texture/Texture.java @@ -121,11 +121,11 @@ public Texture flipY() { } public Texture zoomIn(double scalar) { - return uvBox(uvBox().scaleCentered(1 / scalar)); + return uvBox(uvBox().scaleCenter(1 / scalar)); } public Texture zoomOut(double scalar) { - return uvBox(uvBox().scaleCentered(scalar)); + return uvBox(uvBox().scaleCenter(scalar)); } public Texture shift(Vector shift) {