Switch energy counter chart guiding line to clustering too
This commit is contained in:
parent
7664b0b4de
commit
ac2c387601
@ -14,13 +14,19 @@ import ru.dbotthepony.mc.otm.client.minecraft
|
|||||||
import ru.dbotthepony.mc.otm.client.render.*
|
import ru.dbotthepony.mc.otm.client.render.*
|
||||||
import ru.dbotthepony.mc.otm.core.TextComponent
|
import ru.dbotthepony.mc.otm.core.TextComponent
|
||||||
import ru.dbotthepony.kommons.math.RGBAColor
|
import ru.dbotthepony.kommons.math.RGBAColor
|
||||||
|
import ru.dbotthepony.mc.otm.core.RandomSource2Generator
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
import ru.dbotthepony.mc.otm.core.math.asAngle
|
import ru.dbotthepony.mc.otm.core.math.asAngle
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.clusterize
|
||||||
import ru.dbotthepony.mc.otm.core.util.formatPower
|
import ru.dbotthepony.mc.otm.core.util.formatPower
|
||||||
import ru.dbotthepony.mc.otm.core.math.times
|
import ru.dbotthepony.mc.otm.core.math.times
|
||||||
|
import java.util.Random
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<EnergyCounterBlockEntity> {
|
class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<EnergyCounterBlockEntity> {
|
||||||
|
private val random = Random()
|
||||||
|
|
||||||
override fun render(
|
override fun render(
|
||||||
tile: EnergyCounterBlockEntity,
|
tile: EnergyCounterBlockEntity,
|
||||||
p_112308_: Float,
|
p_112308_: Float,
|
||||||
@ -80,7 +86,7 @@ class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Con
|
|||||||
|
|
||||||
if (maximum.isZero || maximum.isInfinite) {
|
if (maximum.isZero || maximum.isInfinite) {
|
||||||
normalized = FloatArray(chart.width) {
|
normalized = FloatArray(chart.width) {
|
||||||
if (chart[it].isInfinite) 0.8f else 0.0f
|
if (chart[it].isInfinite) 1f else 0.0f
|
||||||
}
|
}
|
||||||
|
|
||||||
levelLabels = ChartLevelLabels(
|
levelLabels = ChartLevelLabels(
|
||||||
@ -97,9 +103,25 @@ class EnergyCounterRenderer(private val context: BlockEntityRendererProvider.Con
|
|||||||
val map = Float2ObjectArrayMap<Component>()
|
val map = Float2ObjectArrayMap<Component>()
|
||||||
|
|
||||||
map[1f] = maximum.formatPower()
|
map[1f] = maximum.formatPower()
|
||||||
map[0.75f] = (maximum * 0.75).formatPower()
|
|
||||||
map[0.5f] = (maximum * 0.5).formatPower()
|
for (cluster in chart.asIterable().clusterize(random)) {
|
||||||
map[0.25f] = (maximum * 0.25).formatPower()
|
val perc = (cluster.center / maximum).toFloat()
|
||||||
|
|
||||||
|
if (map.keys.none { (it - perc).absoluteValue < 0.08f }) {
|
||||||
|
map[perc] = cluster.center.formatPower()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (map.keys.none { (it - 0.75f).absoluteValue < 0.1f })
|
||||||
|
map[0.75f] = (maximum * 0.75).formatPower()
|
||||||
|
|
||||||
|
if (map.keys.none { (it - 0.5f).absoluteValue < 0.1f })
|
||||||
|
map[0.5f] = (maximum * 0.5).formatPower()
|
||||||
|
|
||||||
|
if (map.keys.none { (it - 0.25f).absoluteValue < 0.1f })
|
||||||
|
map[0.25f] = (maximum * 0.25).formatPower()
|
||||||
|
*/
|
||||||
|
|
||||||
levelLabels = ChartLevelLabels(
|
levelLabels = ChartLevelLabels(
|
||||||
labels = map,
|
labels = map,
|
||||||
|
@ -8,11 +8,15 @@ 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.RandomSource2Generator
|
||||||
import ru.dbotthepony.mc.otm.core.chart.DecimalHistoryChart
|
import ru.dbotthepony.mc.otm.core.chart.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.TranslatableComponent
|
||||||
import ru.dbotthepony.mc.otm.core.math.Decimal
|
import ru.dbotthepony.mc.otm.core.math.Decimal
|
||||||
|
import ru.dbotthepony.mc.otm.core.math.clusterize
|
||||||
|
import ru.dbotthepony.mc.otm.core.util.formatPower
|
||||||
import ru.dbotthepony.mc.otm.core.util.formatTickDuration
|
import ru.dbotthepony.mc.otm.core.util.formatTickDuration
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>(
|
open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>(
|
||||||
screen: S,
|
screen: S,
|
||||||
@ -31,25 +35,41 @@ open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>(
|
|||||||
|
|
||||||
if (maximum.isZero || maximum.isInfinite) {
|
if (maximum.isZero || maximum.isInfinite) {
|
||||||
normalized = FloatArray(chart.width) {
|
normalized = FloatArray(chart.width) {
|
||||||
if (chart[it].isInfinite) 0.8f else 0.0f
|
if (chart[it].isInfinite) 1f else 0.0f
|
||||||
}
|
}
|
||||||
|
|
||||||
levelLabels = ChartLevelLabels(
|
levelLabels = ChartLevelLabels(
|
||||||
labels = mapOf(
|
labels = mapOf(
|
||||||
0.8f to TextComponent("∞"),
|
1f to TextComponent("∞"),
|
||||||
// 0f to formatText(Decimal.ZERO),
|
// 0f to formatText(Decimal.ZERO),
|
||||||
),
|
),
|
||||||
font = font
|
font = font
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
normalized = FloatArray(chart.width) { (chart[it] / maximum).toFloat() * 0.9f }
|
normalized = FloatArray(chart.width) { (chart[it] / maximum).toFloat() }
|
||||||
|
|
||||||
val map = Float2ObjectArrayMap<Component>()
|
val map = Float2ObjectArrayMap<Component>()
|
||||||
|
|
||||||
map[0.9f] = formatText(maximum)
|
map[1f] = formatText(maximum)
|
||||||
map[0.9f * 0.75f] = formatText(maximum * 0.75)
|
|
||||||
map[0.9f * 0.5f] = formatText(maximum * 0.5)
|
for (cluster in chart.asIterable().clusterize(randomGenerator)) {
|
||||||
map[0.9f * 0.25f] = formatText(maximum * 0.25)
|
val perc = (cluster.center / maximum).toFloat()
|
||||||
|
|
||||||
|
if (map.keys.none { (it - perc).absoluteValue < 0.08f }) {
|
||||||
|
map[perc] = cluster.center.formatPower()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (map.keys.none { (it - 0.75f).absoluteValue < 0.1f })
|
||||||
|
map[0.75f] = formatText(maximum * 0.75)
|
||||||
|
|
||||||
|
if (map.keys.none { (it - 0.5f).absoluteValue < 0.1f })
|
||||||
|
map[0.5f] = formatText(maximum * 0.5)
|
||||||
|
|
||||||
|
if (map.keys.none { (it - 0.25f).absoluteValue < 0.1f })
|
||||||
|
map[0.25f] = formatText(maximum * 0.25)
|
||||||
|
*/
|
||||||
|
|
||||||
levelLabels = ChartLevelLabels(
|
levelLabels = ChartLevelLabels(
|
||||||
labels = map,
|
labels = map,
|
||||||
@ -63,7 +83,8 @@ open class DecimalHistoryChartPanel<out S : MatteryScreen<*>>(
|
|||||||
graphics.pose,
|
graphics.pose,
|
||||||
listOf(normalized to RGBAColor.WHITE),
|
listOf(normalized to RGBAColor.WHITE),
|
||||||
width,
|
width,
|
||||||
height,
|
height - font.lineHeight - 4f,
|
||||||
|
y = font.lineHeight.toFloat() + 4f,
|
||||||
labels = ChartMouseLabels(
|
labels = ChartMouseLabels(
|
||||||
mouseX - absoluteX,
|
mouseX - absoluteX,
|
||||||
mouseY - absoluteY,
|
mouseY - absoluteY,
|
||||||
|
@ -26,11 +26,13 @@ import ru.dbotthepony.mc.otm.client.render.popScissorRect
|
|||||||
import ru.dbotthepony.mc.otm.client.render.pushScissorRect
|
import ru.dbotthepony.mc.otm.client.render.pushScissorRect
|
||||||
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
|
||||||
import ru.dbotthepony.mc.otm.client.screen.panels.input.QueryUserPanel
|
import ru.dbotthepony.mc.otm.client.screen.panels.input.QueryUserPanel
|
||||||
|
import ru.dbotthepony.mc.otm.core.RandomSource2Generator
|
||||||
import ru.dbotthepony.mc.otm.core.collect.concatIterators
|
import ru.dbotthepony.mc.otm.core.collect.concatIterators
|
||||||
import ru.dbotthepony.mc.otm.core.collect.flatMap
|
import ru.dbotthepony.mc.otm.core.collect.flatMap
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.CopyOnWriteArrayList
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
|
import java.util.random.RandomGenerator
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@ -144,6 +146,10 @@ open class EditablePanel<out S : Screen>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val randomGenerator: RandomGenerator by lazy {
|
||||||
|
RandomSource2Generator(random)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bigger values means lesser priority while docking, rendering and processing inputs.
|
* Bigger values means lesser priority while docking, rendering and processing inputs.
|
||||||
*/
|
*/
|
||||||
|
@ -525,14 +525,14 @@ class AndroidStationScreen(p_97741_: AndroidStationMenu, p_97742_: Inventory, p_
|
|||||||
|
|
||||||
if (isPreview && !layoutInvalidated) {
|
if (isPreview && !layoutInvalidated) {
|
||||||
if (firstTick) {
|
if (firstTick) {
|
||||||
scroller.init.invoke(this, RandomSource2Generator(random))
|
scroller.init.invoke(this, randomGenerator)
|
||||||
}
|
}
|
||||||
|
|
||||||
val status = scroller.scroll.invoke(this, RandomSource2Generator(random))
|
val status = scroller.scroll.invoke(this, randomGenerator)
|
||||||
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
scroller = PreviewScrollers.entries.let { it[random.nextInt(it.size)] }
|
scroller = PreviewScrollers.entries.let { it[random.nextInt(it.size)] }
|
||||||
scroller.init.invoke(this, RandomSource2Generator(random))
|
scroller.init.invoke(this, randomGenerator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package ru.dbotthepony.mc.otm.core.math
|
package ru.dbotthepony.mc.otm.core.math
|
||||||
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.filter
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||||
import ru.dbotthepony.mc.otm.core.collect.map
|
|
||||||
import ru.dbotthepony.mc.otm.core.collect.toList
|
|
||||||
import ru.dbotthepony.mc.otm.core.random
|
import ru.dbotthepony.mc.otm.core.random
|
||||||
import java.util.random.RandomGenerator
|
import java.util.random.RandomGenerator
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
interface Cluster<V : Any> {
|
interface Cluster<V : Any> {
|
||||||
val values: List<V>
|
val values: List<V>
|
||||||
@ -35,8 +34,8 @@ private class ClusterValue<V : Comparable<V>>(val value: V, var cluster: Mutable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MutableCluster<V : Comparable<V>>(var center: V) {
|
private class MutableCluster<V : Comparable<V>>(var center: V, expectedSize: Int) {
|
||||||
val values = ArrayList<ClusterValue<V>>()
|
val values = ObjectArrayList<ClusterValue<V>>(min(100, expectedSize))
|
||||||
|
|
||||||
inline fun calculateCenter(identity: V, plus: (V, V) -> V, minus: (V, V) -> V, divInt: (V, Int) -> V, abs: (V) -> V): Boolean {
|
inline fun calculateCenter(identity: V, plus: (V, V) -> V, minus: (V, V) -> V, divInt: (V, Int) -> V, abs: (V) -> V): Boolean {
|
||||||
if (values.isEmpty()) return false
|
if (values.isEmpty()) return false
|
||||||
@ -69,6 +68,7 @@ private inline fun <V : Comparable<V>> Iterable<V>.clusterize(
|
|||||||
if (!itr.hasNext())
|
if (!itr.hasNext())
|
||||||
return listOf()
|
return listOf()
|
||||||
|
|
||||||
|
var expectedSize = 1
|
||||||
var min = itr.next()
|
var min = itr.next()
|
||||||
var max = min
|
var max = min
|
||||||
|
|
||||||
@ -77,6 +77,7 @@ private inline fun <V : Comparable<V>> Iterable<V>.clusterize(
|
|||||||
|
|
||||||
min = minOf(min, value)
|
min = minOf(min, value)
|
||||||
max = maxOf(max, value)
|
max = maxOf(max, value)
|
||||||
|
expectedSize++
|
||||||
}
|
}
|
||||||
|
|
||||||
if (min == max) {
|
if (min == max) {
|
||||||
@ -84,15 +85,18 @@ private inline fun <V : Comparable<V>> Iterable<V>.clusterize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var targetClusters = initialClusters
|
var targetClusters = initialClusters
|
||||||
|
val clusters = ObjectArrayList<MutableCluster<V>>(initialClusters)
|
||||||
|
val values = ObjectArrayList<ClusterValue<V>>(expectedSize)
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
val clusters = ArrayList<MutableCluster<V>>()
|
clusters.clear()
|
||||||
val values = ArrayList<ClusterValue<V>>()
|
values.clear()
|
||||||
|
|
||||||
var converged = false
|
var converged = false
|
||||||
var overSaturated = false
|
var oversaturation = false
|
||||||
|
|
||||||
for (i in 0 until targetClusters) {
|
for (i in 0 until targetClusters) {
|
||||||
clusters.add(MutableCluster(identity))
|
clusters.add(MutableCluster(identity, expectedSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
for (value in this) {
|
for (value in this) {
|
||||||
@ -112,7 +116,7 @@ private inline fun <V : Comparable<V>> Iterable<V>.clusterize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!converged) {
|
if (!converged) {
|
||||||
val emptyClusters = ArrayList<MutableCluster<V>>()
|
val emptyClusters = ObjectArrayList<MutableCluster<V>>()
|
||||||
val citr = clusters.iterator()
|
val citr = clusters.iterator()
|
||||||
|
|
||||||
for (cluster in citr) {
|
for (cluster in citr) {
|
||||||
@ -143,7 +147,7 @@ private inline fun <V : Comparable<V>> Iterable<V>.clusterize(
|
|||||||
candidate.error = identity
|
candidate.error = identity
|
||||||
clusters.add(cluster)
|
clusters.add(cluster)
|
||||||
} else {
|
} else {
|
||||||
overSaturated = true
|
oversaturation = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,18 +156,20 @@ private inline fun <V : Comparable<V>> Iterable<V>.clusterize(
|
|||||||
|
|
||||||
val maxError = values.maxOf { it.error }
|
val maxError = values.maxOf { it.error }
|
||||||
|
|
||||||
if (!overSaturated && targetClusters < values.size && heuristics(min, max, maxError)) {
|
if (!oversaturation && targetClusters < values.size && heuristics(min, max, maxError)) {
|
||||||
targetClusters++
|
targetClusters++
|
||||||
} else {
|
} else {
|
||||||
return clusters.iterator()
|
return clusters.stream()
|
||||||
.filter { it.values.isNotEmpty() }
|
.filter { it.values.isNotEmpty() }
|
||||||
.map { Cluster.Impl(it.values.map { it.value }, it.center) }
|
.map { Cluster.Impl(it.values.map { it.value }, it.center) }
|
||||||
|
.sorted { o1, o2 -> o2.values.size.compareTo(o1.values.size) } // большие кластеры должны идти первыми
|
||||||
.toList()
|
.toList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val DECIMAL_ERROR_TOLERANCE = Decimal("0.04")
|
// TODO: could use some tweaking
|
||||||
|
private val DECIMAL_ERROR_TOLERANCE = Decimal("0.02")
|
||||||
|
|
||||||
fun Iterable<Decimal>.clusterize(random: RandomGenerator, clusters: Int? = null): List<Cluster<Decimal>> {
|
fun Iterable<Decimal>.clusterize(random: RandomGenerator, clusters: Int? = null): List<Cluster<Decimal>> {
|
||||||
return clusterize(random, clusters ?: 1, Decimal.ZERO, Decimal::plus, Decimal::minus, Decimal::div, Decimal::absoluteValue) { min, max, error ->
|
return clusterize(random, clusters ?: 1, Decimal.ZERO, Decimal::plus, Decimal::minus, Decimal::div, Decimal::absoluteValue) { min, max, error ->
|
||||||
|
Loading…
Reference in New Issue
Block a user