|
17 | 17 |
|
18 | 18 | package com.lambda.gui.impl.clickgui.module.settings.impl |
19 | 19 |
|
| 20 | +import com.lambda.graphics.renderer.gui.TextureRenderer |
| 21 | +import com.lambda.graphics.shader.Shader.Companion.shader |
| 22 | +import com.lambda.gui.component.core.FilledRect.Companion.rect |
| 23 | +import com.lambda.gui.component.core.OutlineRect.Companion.outline |
20 | 24 | import com.lambda.gui.component.core.UIBuilder |
21 | 25 | import com.lambda.gui.component.layout.Layout |
22 | | -import com.lambda.gui.component.popup.Popup |
23 | 26 | import com.lambda.gui.impl.clickgui.module.settings.SettingLayout |
| 27 | +import com.lambda.gui.impl.clickgui.module.settings.impl.NumberSlider.Companion.numberSlider |
| 28 | +import com.lambda.module.modules.client.ClickGui |
24 | 29 | import com.lambda.util.Mouse |
| 30 | +import com.lambda.util.StringUtils.capitalize |
| 31 | +import com.lambda.util.math.MathUtils.toDegree |
| 32 | +import com.lambda.util.math.MathUtils.toRadian |
| 33 | +import com.lambda.util.math.Vec2d |
| 34 | +import com.lambda.util.math.a |
| 35 | +import com.lambda.util.math.lerp |
| 36 | +import com.lambda.util.math.multAlpha |
| 37 | +import com.lambda.util.math.setAlpha |
| 38 | +import com.lambda.util.math.transform |
| 39 | +import net.minecraft.util.math.Vec3d |
25 | 40 | import java.awt.Color |
| 41 | +import kotlin.math.atan2 |
| 42 | +import kotlin.math.cos |
| 43 | +import kotlin.math.hypot |
| 44 | +import kotlin.math.sin |
| 45 | +import kotlin.math.sqrt |
26 | 46 | import kotlin.reflect.KMutableProperty0 |
27 | 47 |
|
28 | 48 | class ColorPicker( |
29 | 49 | owner: Layout, |
30 | 50 | name: String, |
31 | 51 | field: KMutableProperty0<Color>, |
32 | | -) : SettingLayout<Color>(owner, name, field) { |
33 | | - private val popup = Popup(this, name).apply { |
34 | | - window.use { |
35 | | - windowWidth = 200.0 |
36 | | - windowHeight = 100.0 |
37 | | - } |
| 52 | +) : SettingLayout<Color>(owner, name, field, true) { |
| 53 | + |
| 54 | + private var hsb get() = Color.RGBtoHSB(settingDelegate.red, settingDelegate.green, settingDelegate.blue, null).let { |
| 55 | + Vec3d(it[0].toDouble(), it[1].toDouble(), it[2].toDouble()) |
| 56 | + }; set(value) { |
| 57 | + settingDelegate = Color(Color.HSBtoRGB(value.x.toFloat(), value.y.toFloat(), value.z.toFloat())).setAlpha(alpha) |
| 58 | + } |
| 59 | + |
| 60 | + private var hue get() = hsb.x; set(value) { |
| 61 | + val cache = hsb |
| 62 | + if (cache.x == value) return |
| 63 | + hsb = Vec3d(value, cache.y, cache.z) |
| 64 | + } |
| 65 | + |
| 66 | + private var saturation get() = hsb.y; set(value) { |
| 67 | + val cache = hsb |
| 68 | + if (cache.y == value) return |
| 69 | + hsb = Vec3d(cache.x, value, cache.z) |
| 70 | + } |
| 71 | + |
| 72 | + private var brightness get() = hsb.z; set(value) { |
| 73 | + val cache = hsb |
| 74 | + if (cache.z == value) return |
| 75 | + hsb = Vec3d(cache.x, cache.y, value) |
| 76 | + } |
| 77 | + |
| 78 | + private var alpha get() = settingDelegate.a; set(value) { |
| 79 | + settingDelegate = settingDelegate.setAlpha(value) |
38 | 80 | } |
39 | 81 |
|
40 | 82 | init { |
41 | | - onMouseAction(Mouse.Button.Left) { |
42 | | - popup.show() |
| 83 | + rect { |
| 84 | + val shrink = 3.0 |
| 85 | + |
| 86 | + onUpdate { |
| 87 | + rect = titleBar.rect |
| 88 | + .moveFirst(Vec2d.RIGHT * (titleBar.width - titleBar.height)) |
| 89 | + .shrink(shrink + (1.0 - showAnimation)) + |
| 90 | + Vec2d.RIGHT * lerp(showAnimation, 5.0, -ClickGui.fontOffset + shrink) |
| 91 | + |
| 92 | + setColor(settingDelegate.multAlpha(showAnimation)) |
| 93 | + setRadius(100.0) |
| 94 | + } |
| 95 | + } |
| 96 | + |
| 97 | + content.layout { |
| 98 | + val getU = { transform(mousePosition.x, positionX, positionX + width, 0.0, 1.0) } |
| 99 | + val getV = { transform(mousePosition.y, positionY, positionY + height, 0.0, 1.0) } |
| 100 | + |
| 101 | + onUpdate { |
| 102 | + width = content.width * 0.5 |
| 103 | + positionX = content.positionX + (content.width - width) * 0.5 |
| 104 | + height = width |
| 105 | + } |
| 106 | + |
| 107 | + onRender { |
| 108 | + hueCircleShader.use() |
| 109 | + TextureRenderer.drawInternal(rect) |
| 110 | + } |
| 111 | + |
| 112 | + onMouseMove { |
| 113 | + if (pressedButton != Mouse.Button.Left) return@onMouseMove |
| 114 | + |
| 115 | + val (u, v) = getU() to getV() |
| 116 | + hue = uvToHue(u, v).div(360.0).coerceIn(0.0, 1.0) |
| 117 | + saturation = hypot(u - 0.5, v - 0.5).coerceIn(0.0, 0.5) * 2 |
| 118 | + } |
| 119 | + |
| 120 | + val circle = this |
| 121 | + val knobSize = 2.0 |
| 122 | + |
| 123 | + outline { |
| 124 | + width = knobSize; height = knobSize |
| 125 | + |
| 126 | + setColor(Color.BLACK) |
| 127 | + glowRadius = 1.0 |
| 128 | + roundRadius = 100.0 |
| 129 | + |
| 130 | + onUpdate { |
| 131 | + val uv = if (circle.pressedButton == Mouse.Button.Left) clampToCircle(Vec2d(getU(), getV())) |
| 132 | + else hueToUv(hue * 360, saturation) |
| 133 | + |
| 134 | + positionX = transform(uv.x, 0.0, 1.0, circle.positionX, circle.positionX + circle.width) - knobSize * 0.5 |
| 135 | + positionY = transform(uv.y, 0.0, 1.0, circle.positionY, circle.positionY + circle.height) - knobSize * 0.5 |
| 136 | + } |
| 137 | + } |
| 138 | + } |
| 139 | + |
| 140 | + listOf(::brightness, ::alpha).map { |
| 141 | + content.numberSlider(it.name.capitalize(), "", 0.0, 1.0, 0.01, it).apply { |
| 142 | + forceRoundDisplayValue = true |
| 143 | + } |
| 144 | + }.onEach { |
| 145 | + it.onUpdate { |
| 146 | + width = this@ColorPicker.content.width |
| 147 | + } |
43 | 148 | } |
| 149 | + |
| 150 | + content.listify() |
44 | 151 | } |
45 | 152 |
|
46 | 153 | companion object { |
| 154 | + private val hueCircleShader = shader("renderer/hue_circle") |
| 155 | + |
| 156 | + private fun uvToHue(u: Double, v: Double): Double { |
| 157 | + val x = (u - 0.5) * 2.0 |
| 158 | + val y = ((1.0 - v) - 0.5) * -2.0 |
| 159 | + |
| 160 | + var hue = atan2(y, x).toDegree() + 90.0 |
| 161 | + if (hue < 0) hue += 360.0 |
| 162 | + return hue |
| 163 | + } |
| 164 | + |
| 165 | + private fun hueToUv(hue: Double, radius: Double): Vec2d { |
| 166 | + val angle = (hue - 90.0).toRadian() |
| 167 | + |
| 168 | + val x = cos(angle) * radius |
| 169 | + val y = sin(angle) * radius |
| 170 | + |
| 171 | + val u = (x * 0.5f) + 0.5f |
| 172 | + val v = (y * -0.5f) + 0.5f |
| 173 | + |
| 174 | + return Vec2d(u, 1.0 - v) |
| 175 | + } |
| 176 | + |
| 177 | + private fun clampToCircle(uv: Vec2d): Vec2d { |
| 178 | + val center = Vec2d(0.5f, 0.5f) |
| 179 | + val radius = 0.5f |
| 180 | + val dx = uv.x- center.x |
| 181 | + val dy = uv.y - center.y |
| 182 | + val dist = sqrt(dx * dx + dy * dy) |
| 183 | + |
| 184 | + return if (dist > radius) { |
| 185 | + val scale = radius / dist |
| 186 | + Vec2d(center.x + dx * scale, center.y + dy * scale) |
| 187 | + } else uv |
| 188 | + } |
| 189 | + |
47 | 190 | /** |
48 | 191 | * Creates a [ColorPicker] |
49 | 192 | */ |
|
0 commit comments