Compare commits

...

2 Commits

Author SHA1 Message Date
cea43a6ac6
Chart "ago" label 2024-10-01 19:57:11 +07:00
184203ee20
Chart levels 2024-10-01 15:13:42 +07:00
5 changed files with 119 additions and 26 deletions

View File

@ -827,6 +827,8 @@ private fun androidFeatures(provider: MatteryLanguageProvider) {
private fun gui(provider: MatteryLanguageProvider) { private fun gui(provider: MatteryLanguageProvider) {
with(provider.english) { with(provider.english) {
gui("ago", "%s ago")
gui("part_of_multiblock", "Part of multiblock structure, useless on its own") gui("part_of_multiblock", "Part of multiblock structure, useless on its own")
gui("quicksearch", "Quick search...") gui("quicksearch", "Quick search...")

View File

@ -832,6 +832,8 @@ private fun androidFeatures(provider: MatteryLanguageProvider) {
private fun gui(provider: MatteryLanguageProvider) { private fun gui(provider: MatteryLanguageProvider) {
with(provider.russian) { with(provider.russian) {
gui("ago", "%s тому назад")
gui("part_of_multiblock", "Часть мультиблока, бесполезен сам по себе") gui("part_of_multiblock", "Часть мультиблока, бесполезен сам по себе")
gui("quicksearch", "Быстрый поиск...") gui("quicksearch", "Быстрый поиск...")

View File

@ -1,13 +1,17 @@
package ru.dbotthepony.mc.otm.client.render package ru.dbotthepony.mc.otm.client.render
import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.BufferBuilder
import com.mojang.blaze3d.vertex.BufferUploader import com.mojang.blaze3d.vertex.BufferUploader
import com.mojang.blaze3d.vertex.ByteBufferBuilder
import com.mojang.blaze3d.vertex.DefaultVertexFormat import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.blaze3d.vertex.VertexFormat import com.mojang.blaze3d.vertex.VertexFormat
import it.unimi.dsi.fastutil.floats.Float2ObjectMap
import net.minecraft.client.gui.Font import net.minecraft.client.gui.Font
import net.minecraft.client.renderer.GameRenderer import net.minecraft.client.renderer.GameRenderer
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import org.lwjgl.opengl.GL11.GL_LESS
import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.kommons.math.RGBAColor
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.acos import kotlin.math.acos
@ -20,11 +24,13 @@ private const val LINE_WIDTH = 1f
private const val HIGHLIGHT_WIDTH = 3f private const val HIGHLIGHT_WIDTH = 3f
private val TEXT_BACKGROUND = RGBAColor(0f, 0f, 0f, 0.5f) private val TEXT_BACKGROUND = RGBAColor(0f, 0f, 0f, 0.5f)
private val LINE_COLOR = RGBAColor(1f, 0f, 1f, 0.25f)
private val LINE_TEXT_COLOR = RGBAColor(1f, 0f, 1f, 0.5f)
data class ChartMouseLabels( data class ChartMouseLabels(
val mouseX: Float, val mouseX: Float,
val mouseY: Float, val mouseY: Float,
val labels: (Int) -> Component, val labels: (Int) -> List<Component>,
val font: Font, val font: Font,
val color: RGBAColor = RGBAColor.WHITE, val color: RGBAColor = RGBAColor.WHITE,
val pillarColor: RGBAColor = RGBAColor.WHITE, val pillarColor: RGBAColor = RGBAColor.WHITE,
@ -32,6 +38,17 @@ data class ChartMouseLabels(
val textBackgroundColor: RGBAColor = TEXT_BACKGROUND val textBackgroundColor: RGBAColor = TEXT_BACKGROUND
) )
data class ChartLevelLabels(
val labels: Map<Float, Component>,
val font: Font,
val lineColor: RGBAColor = RGBAColor.DARK_GREEN,
val textColor: RGBAColor = RGBAColor.DARK_GREEN,
val textGravity: RenderGravity = RenderGravity.TOP_RIGHT,
val textOutline: RGBAColor = RGBAColor.BLACK
)
private val BYTE_BUFFER_BUILDER = ByteBufferBuilder(786_432)
fun renderChart( fun renderChart(
poseStack: PoseStack, poseStack: PoseStack,
normalized: FloatArray, normalized: FloatArray,
@ -39,10 +56,11 @@ fun renderChart(
height: Float, height: Float,
color: RGBAColor = RGBAColor.WHITE, color: RGBAColor = RGBAColor.WHITE,
labels: ChartMouseLabels? = null, labels: ChartMouseLabels? = null,
levelLabels: ChartLevelLabels? = null,
x: Float = 0f, x: Float = 0f,
y: Float = 0f, y: Float = 0f,
) { ) {
val builder = tesselator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION) val builder = BufferBuilder(BYTE_BUFFER_BUILDER, VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR)
val step = width / normalized.size val step = width / normalized.size
val pose = poseStack.last().pose val pose = poseStack.last().pose
@ -52,6 +70,17 @@ fun renderChart(
var drawPointX = 0f var drawPointX = 0f
var drawPointY = 0f var drawPointY = 0f
if (levelLabels != null) {
for ((level, label) in levelLabels.labels) {
val y0 = y + (1f - level) * height
builder.vertex(pose, x + width, LINE_WIDTH / 2f + y0, 0f).color(levelLabels.lineColor)
builder.vertex(pose, x + width, -LINE_WIDTH / 2f + y0, 0f).color(levelLabels.lineColor)
builder.vertex(pose, x, -LINE_WIDTH / 2f + y0, 0f).color(levelLabels.lineColor)
builder.vertex(pose, x, LINE_WIDTH / 2f + y0, 0f).color(levelLabels.lineColor)
}
}
for (i in 0 until normalized.size - 1) { for (i in 0 until normalized.size - 1) {
val y0 = y + (1f - normalized[i]) * height val y0 = y + (1f - normalized[i]) * height
val y1 = y + (1f - normalized[i + 1]) * height val y1 = y + (1f - normalized[i + 1]) * height
@ -63,10 +92,10 @@ fun renderChart(
val yDiff = y1 - y0 val yDiff = y1 - y0
if (yDiff.sign == 0f) { if (yDiff.sign == 0f) {
builder.vertex(pose, x0, y0 + LINE_WIDTH / 2f, 0f) builder.vertex(pose, x0, y0 + LINE_WIDTH / 2f, 0f).color(color)
builder.vertex(pose, x0, y0 - LINE_WIDTH / 2f, 0f) builder.vertex(pose, x0, y0 - LINE_WIDTH / 2f, 0f).color(color)
builder.vertex(pose, x1, y1 - LINE_WIDTH / 2f, 0f) builder.vertex(pose, x1, y1 - LINE_WIDTH / 2f, 0f).color(color)
builder.vertex(pose, x1, y1 + LINE_WIDTH / 2f, 0f) builder.vertex(pose, x1, y1 + LINE_WIDTH / 2f, 0f).color(color)
} else { } else {
val length = (xDiff.pow(2f) + yDiff.pow(2f)).pow(0.5f) val length = (xDiff.pow(2f) + yDiff.pow(2f)).pow(0.5f)
val angle = acos(xDiff / length) * yDiff.sign - PI / 2f // rotate 90 deg val angle = acos(xDiff / length) * yDiff.sign - PI / 2f // rotate 90 deg
@ -74,10 +103,10 @@ fun renderChart(
val xDisp = cos(angle).toFloat() * LINE_WIDTH / 2f val xDisp = cos(angle).toFloat() * LINE_WIDTH / 2f
val yDisp = sin(angle).toFloat() * LINE_WIDTH / 2f val yDisp = sin(angle).toFloat() * LINE_WIDTH / 2f
builder.vertex(pose, x0 + xDisp, y0 + yDisp, 0f) builder.vertex(pose, x0 + xDisp, y0 + yDisp, 0f).color(color)
builder.vertex(pose, x0 - xDisp, y0 - yDisp, 0f) builder.vertex(pose, x0 - xDisp, y0 - yDisp, 0f).color(color)
builder.vertex(pose, x1 - xDisp, y1 - yDisp, 0f) builder.vertex(pose, x1 - xDisp, y1 - yDisp, 0f).color(color)
builder.vertex(pose, x1 + xDisp, y1 + yDisp, 0f) builder.vertex(pose, x1 + xDisp, y1 + yDisp, 0f).color(color)
} }
//graphics.renderRect(x0, y0, LINE_WIDTH, LINE_WIDTH) //graphics.renderRect(x0, y0, LINE_WIDTH, LINE_WIDTH)
@ -89,25 +118,56 @@ fun renderChart(
} }
} }
RenderSystem.setShader(GameRenderer::getPositionShader) RenderSystem.setShader(GameRenderer::getPositionColorShader)
RenderSystem.setShaderColor(color.red, color.green, color.blue, color.alpha) // RenderSystem.setShaderColor(color.red, color.green, color.blue, color.alpha)
RenderSystem.disableDepthTest() RenderSystem.disableDepthTest()
RenderSystem.disableBlend() RenderSystem.disableBlend()
RenderSystem.setShaderColor(1f, 1f, 1f, 1f)
BufferUploader.drawWithShader(builder.buildOrThrow()) BufferUploader.drawWithShader(builder.buildOrThrow())
RenderSystem.enableDepthTest()
RenderSystem.depthFunc(GL_LESS)
if (levelLabels != null && levelLabels.labels.isNotEmpty()) {
for ((level, label) in levelLabels.labels) {
val y0 = y + (1f - level) * height
val tX = x + levelLabels.textGravity.repositionX(width, levelLabels.font.width(label).toFloat()) - 1f
val tY = y0 - LINE_WIDTH - 1f - levelLabels.font.lineHeight + levelLabels.textGravity.repositionY(levelLabels.font.lineHeight * 2f + LINE_WIDTH + 2f, levelLabels.font.lineHeight.toFloat()) + 1f
levelLabels.font.draw(
poseStack,
label,
x = tX,
y = tY,
color = levelLabels.textColor,
drawOutline = true,
outlineZ = -1f,
outlineColor = levelLabels.textOutline
)
}
}
if (drawLabel != -1) { if (drawLabel != -1) {
renderRect(pose, drawPointX - LINE_WIDTH / 2f, y, LINE_WIDTH, height, color = labels!!.pillarColor) renderRect(pose, drawPointX - LINE_WIDTH / 2f, y, LINE_WIDTH, height, color = labels!!.pillarColor)
renderRect(pose, drawPointX - HIGHLIGHT_WIDTH / 2f, drawPointY - HIGHLIGHT_WIDTH / 2f, HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, color = labels.color) renderRect(pose, drawPointX - HIGHLIGHT_WIDTH / 2f, drawPointY - HIGHLIGHT_WIDTH / 2f, HIGHLIGHT_WIDTH, HIGHLIGHT_WIDTH, color = labels.color)
val label = labels.labels(drawLabel) val label = labels.labels(drawLabel)
val fWidth = labels.font.width(label).toFloat() + 2f
val fHeight = labels.font.lineHeight.toFloat() + 2f
val anchorX = labels.mouseX // drawPointX if (label.isNotEmpty()) {
val anchorY = labels.mouseY - fHeight / 2f - 2f val fWidth = label.maxOf { labels.font.width(it).toFloat() + 2f }
val fHeight = labels.font.lineHeight.toFloat() * label.size + 2f
renderRect(pose, anchorX - fWidth / 2f, anchorY - fHeight / 2f, fWidth, fHeight, color = labels.textBackgroundColor) val anchorX = labels.mouseX // drawPointX
labels.font.draw(poseStack, label, anchorX, anchorY, gravity = RenderGravity.CENTER_CENTER, color = labels.textColor) var anchorY = labels.mouseY - fHeight - 2f
renderRect(pose, anchorX - fWidth / 2f, anchorY - 2f, fWidth, fHeight + 4f, color = labels.textBackgroundColor)
label.forEach {
labels.font.draw(poseStack, it, anchorX, anchorY, gravity = RenderGravity.TOP_CENTER, color = labels.textColor)
anchorY += labels.font.lineHeight
}
}
} }
} }

View File

@ -189,8 +189,8 @@ private fun Font.drawInternal(
for ((_x, _y) in outlinePositions) { for ((_x, _y) in outlinePositions) {
drawInBatch( drawInBatch(
text, text,
x * inv + _x, (x + _x) * inv,
y * inv + _y, (y + _y) * inv,
outlineColor.toBGR(), outlineColor.toBGR(),
drawShadow, drawShadow,
poseStack.last().pose(), poseStack.last().pose(),
@ -251,7 +251,7 @@ fun Font.draw(
rounding: GravityRounding = if (scale == 1f) GravityRounding.DEFAULT else GravityRounding.NO, rounding: GravityRounding = if (scale == 1f) GravityRounding.DEFAULT else GravityRounding.NO,
drawOutline: Boolean = false, drawOutline: Boolean = false,
outlineColor: RGBAColor = RGBAColor.BLACK, outlineColor: RGBAColor = RGBAColor.BLACK,
outlineZ: Float = 0.1f, outlineZ: Float = -0.1f,
): Float { ): Float {
return drawInternal( return drawInternal(
poseStack = poseStack, poseStack = poseStack,
@ -294,7 +294,7 @@ data class TextIcon(
val customShadow: Boolean = false, val customShadow: Boolean = false,
val drawOutline: Boolean = false, val drawOutline: Boolean = false,
val outlineColor: RGBAColor = RGBAColor.BLACK, val outlineColor: RGBAColor = RGBAColor.BLACK,
val outlineZ: Float = 0.1f, val outlineZ: Float = -0.1f,
) : IGUIRenderable { ) : IGUIRenderable {
override val width: Float = font.width(text) * scale override val width: Float = font.width(text) * scale
override val height: Float = font.lineHeight * scale override val height: Float = font.lineHeight * scale
@ -352,7 +352,7 @@ fun Font.draw(
rounding: GravityRounding = if (scale == 1f) GravityRounding.DEFAULT else GravityRounding.NO, rounding: GravityRounding = if (scale == 1f) GravityRounding.DEFAULT else GravityRounding.NO,
drawOutline: Boolean = false, drawOutline: Boolean = false,
outlineColor: RGBAColor = RGBAColor.BLACK, outlineColor: RGBAColor = RGBAColor.BLACK,
outlineZ: Float = 0.1f, outlineZ: Float = -0.1f,
): Float { ): Float {
return drawInternal( return drawInternal(
poseStack = poseStack, poseStack = poseStack,
@ -400,7 +400,7 @@ fun Font.draw(
rounding: GravityRounding = if (scale == 1f) GravityRounding.DEFAULT else GravityRounding.NO, rounding: GravityRounding = if (scale == 1f) GravityRounding.DEFAULT else GravityRounding.NO,
drawOutline: Boolean = false, drawOutline: Boolean = false,
outlineColor: RGBAColor = RGBAColor.BLACK, outlineColor: RGBAColor = RGBAColor.BLACK,
outlineZ: Float = 0.1f, outlineZ: Float = -0.1f,
): Float { ): Float {
return drawInternal( return drawInternal(
poseStack = poseStack, poseStack = poseStack,

View File

@ -1,14 +1,18 @@
package ru.dbotthepony.mc.otm.client.screen.panels package ru.dbotthepony.mc.otm.client.screen.panels
import it.unimi.dsi.fastutil.floats.Float2ObjectArrayMap
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import ru.dbotthepony.kommons.math.RGBAColor import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.client.render.ChartLevelLabels
import ru.dbotthepony.mc.otm.client.render.ChartMouseLabels import ru.dbotthepony.mc.otm.client.render.ChartMouseLabels
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
import ru.dbotthepony.mc.otm.client.render.renderChart import ru.dbotthepony.mc.otm.client.render.renderChart
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.core.DecimalHistoryChart import ru.dbotthepony.mc.otm.core.DecimalHistoryChart
import ru.dbotthepony.mc.otm.core.TextComponent import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.formatTickDuration
open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>( open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>(
screen: S, screen: S,
@ -23,13 +27,31 @@ open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>(
override fun innerRender(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { override fun innerRender(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
val maximum = graph.max() val maximum = graph.max()
val normalized: FloatArray val normalized: FloatArray
val levelLabels: ChartLevelLabels
if (maximum.isZero || maximum.isInfinite) { if (maximum.isZero || maximum.isInfinite) {
normalized = FloatArray(graph.width) { normalized = FloatArray(graph.width) {
if (graph[it].isInfinite) 0.8f else 0.0f if (graph[it].isInfinite) 0.8f else 0.0f
} }
levelLabels = ChartLevelLabels(
labels = mapOf(0.8f to formatText(maximum)),
font = font
)
} else { } else {
normalized = FloatArray(graph.width) { (graph[it] / maximum).toFloat() * 0.9f } normalized = FloatArray(graph.width) { (graph[it] / maximum).toFloat() * 0.9f }
val map = Float2ObjectArrayMap<Component>()
map[0.9f] = formatText(maximum)
map[0.9f * 0.75f] = formatText(maximum * 0.75)
map[0.9f * 0.5f] = formatText(maximum * 0.5)
map[0.9f * 0.25f] = formatText(maximum * 0.25)
levelLabels = ChartLevelLabels(
labels = map,
font = font
)
} }
graphics.renderRect(0f, 0f, this.width, this.height, color = RGBAColor.BLACK) graphics.renderRect(0f, 0f, this.width, this.height, color = RGBAColor.BLACK)
@ -42,9 +64,16 @@ open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>(
labels = ChartMouseLabels( labels = ChartMouseLabels(
mouseX - absoluteX, mouseX - absoluteX,
mouseY - absoluteY, mouseY - absoluteY,
{ formatText(graph[it]) }, {
listOf(
formatText(graph[it]),
TextComponent(""),
TranslatableComponent("otm.gui.ago", formatTickDuration(it * graph.resolution, true))
)
},
font, font,
) ),
levelLabels = levelLabels
) )
} }