Skip to content

Commit

Permalink
Fix number rendering for small decimals
Browse files Browse the repository at this point in the history
  • Loading branch information
ajalt committed Mar 24, 2024
1 parent d0346a9 commit a2b0eda
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import kotlin.math.roundToInt
/**
* Parse a string representing a CSS color value.
*
* Custom color spaces with dashed identifiers are not currently supported.
*
* @param color The CSS color string to parse
* @param customColorSpaces A list of custom color spaces to recognize in the `color()` function.
* Each pair should be the identifier of the color and its [ColorSpace].
Expand All @@ -27,7 +25,7 @@ import kotlin.math.roundToInt
@JvmOverloads // TODO(4.0) remove this
fun Color.Companion.parse(
color: String,
customColorSpaces: List<Pair<String, ColorSpace<*>>> = emptyList(),
customColorSpaces: Map<String, ColorSpace<*>> = emptyMap(),
): Color {
return parseOrNull(color, customColorSpaces)
?: throw IllegalArgumentException("Invalid color: $color")
Expand All @@ -44,7 +42,7 @@ fun Color.Companion.parse(
@JvmOverloads // TODO(4.0) remove this
fun Color.Companion.parseOrNull(
color: String,
customColorSpaces: List<Pair<String, ColorSpace<*>>> = emptyList(),
customColorSpaces: Map<String, ColorSpace<*>> = emptyMap(),
): Color? {
val keywordColor = CssColors.colorsByName[color]
return when {
Expand Down Expand Up @@ -99,7 +97,7 @@ private object PATTERNS {

private fun color(
match: MatchResult,
customColorSpaces: List<Pair<String, ColorSpace<*>>>,
customColorSpaces: Map<String, ColorSpace<*>>,
): Color? {
val space = when (val name = match.groupValues[1]) {
"srgb" -> SRGB
Expand All @@ -110,7 +108,7 @@ private fun color(
"rec2020" -> BT2020
"xyz", "xyz-d50" -> XYZ50
"xyz-d65" -> XYZ65
else -> customColorSpaces.firstOrNull { it.first == name }?.second
else -> customColorSpaces.entries.firstOrNull { it.key == name }?.value
} ?: return null

val values = match.groupValues[2].split(Regex("\\s+")).map { percentOrNumber(it).clampF() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import com.github.ajalt.colormath.model.RGBColorSpaces.LinearSRGB
import com.github.ajalt.colormath.model.RGBColorSpaces.ROMM_RGB
import com.github.ajalt.colormath.model.XYZColorSpaces.XYZ50
import kotlin.jvm.JvmOverloads
import kotlin.math.absoluteValue
import kotlin.math.pow
import kotlin.math.round
import kotlin.math.roundToInt

enum class RenderCondition {
Expand Down Expand Up @@ -94,9 +94,9 @@ fun Color.formatCssStringOrNull(
alphaPercent: Boolean = false,
legacyName: Boolean = false,
legacyFormat: Boolean = false,
customColorSpaces: List<Pair<String, ColorSpace<*>>> = emptyList(),
customColorSpaces: Map<String, ColorSpace<*>> = emptyMap(),
): String? {
return customColorSpaces.firstOrNull { it.second == space }?.first?.let { spaceName ->
return customColorSpaces.entries.firstOrNull { it.value == space }?.key?.let { spaceName ->
renderFn(spaceName, unitsPercent, alphaPercent, renderAlpha)
} ?: when (this) {
is RGB -> when (space) {
Expand Down Expand Up @@ -156,7 +156,7 @@ fun Color.formatCssStringOrNull(
* ```
*
* ```
* > JzAzBz(0.1, 0.2, 0.3).formatCssString(customColorSpaces= listOf("jzazbz" to JzAzBz))
* > JzAzBz(0.1, 0.2, 0.3).formatCssString(customColorSpaces=listOf("jzazbz" to JzAzBz))
* "color(jzazbz 0.1 0.2 0.3)"
* ```
*
Expand All @@ -177,7 +177,7 @@ fun Color.formatCssString(
alphaPercent: Boolean = false,
legacyName: Boolean = false,
legacyFormat: Boolean = false,
customColorSpaces: List<Pair<String, ColorSpace<*>>> = emptyList(),
customColorSpaces: Map<String, ColorSpace<*>> = emptyMap(),
): String {
return formatCssStringOrNull(
hueUnit,
Expand Down Expand Up @@ -345,14 +345,13 @@ private fun Color.renderAlpha(

private fun Float.render(percent: Boolean = false, precision: Int = 4): String = when (percent) {
true -> "${(this * 100).roundToInt()}%"
false -> when (this) {
0f -> "0"
1f -> "1"
else -> {
val i = toInt()
val d = ((this - i) * (10.0.pow(precision))).roundToInt()
if (d == 0) i.toString() else "$i.$d".trimEnd('0')
}
false -> {
val abs = absoluteValue
val i = abs.toInt()
val sgn = if (this < 0) "-" else ""
val d = ((abs - i) * (10.0.pow(precision))).roundToInt()
if (d == 0) toInt().toString()
else "$sgn$i.${d.toString().padStart(precision, '0').trimEnd('0')}"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ class CssParseTest {
@Test
@JsName("parseCssColor_named")
fun `parseCssColor named`() {
Color.parse(
"color(jzazbz 0.1 0.2 0.3)",
customColorSpaces = listOf("jzazbz" to JzAzBz)
).shouldEqualColor(JzAzBz(.1, .2, .3))
Color.parse("rebeccapurple") shouldBe RGB("#663399")
}

@Test
@JsName("parseCssColor_custom_space")
fun `parseCssColor custom space`() {
Color.parse("rebeccapurple") shouldBe RGB("#663399")
Color.parse(
"color(jzazbz 0.1 0.2 0.3)",
customColorSpaces = mapOf("jzazbz" to JzAzBz)
).shouldEqualColor(JzAzBz(.1, .2, .3))
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,13 @@ class CssRenderTest {
@JsName("formatCssString_custom_space")
fun `formatCssString custom space`() = forAll(
row(ACEScc(.1, .2, .3), "color(acescc 0.1 0.2 0.3)"),
row(JzAzBz(.1, .2, .3), "color(jzazbz 0.1 0.2 0.3)"),
row(JzAzBz(1, -1, -0.5), "color(jzazbz 1 -1 -0.5)"),
row(JzAzBz(0.0162790, -2.91842E-4, -0.00161363), "color(jzazbz 0.0163 -0.0003 -0.0016)"),
row(XYZ55(.1, .2, .3), "color(xyz-d55 0.1 0.2 0.3)"),
row(LUV(.1, .2, .3).toSRGB().toLUV(), "color(luv 0.1 0.2 0.3)"),
) { color, expected ->
color.formatCssString(
customColorSpaces = listOf(
customColorSpaces = mapOf(
"acescc" to ACEScc, "jzazbz" to JzAzBz, "luv" to LUV, "xyz-d55" to XYZ55,
)
) shouldBe expected
Expand Down
11 changes: 10 additions & 1 deletion website/src/wasmJsMain/kotlin/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,11 @@ fun App() {

@Composable
private fun ColumnScope.ControlPanel(vm: ColorPickerViewModel) {
Image(painterResource(Res.drawable.colormath_wordmark), null)
Image(
painter = painterResource(Res.drawable.colormath_wordmark),
contentDescription = null,
modifier = Modifier.clickable(onClick = ::navigateToColormathWebsite)
)
Spacer(Modifier.height(24.dp))
NavigationDrawerItem(
icon = { Icon(Icons.Filled.Add, contentDescription = "Add Gradient") },
Expand Down Expand Up @@ -577,3 +581,8 @@ private fun contrastString(c1: ColormathColor, c2: ColormathColor): String {
val s = c1.wcagContrastRatio(c2).toString()
return s.take(s.indexOf('.') + 3)
}

// js() calls need to be in a separate function in wasm
private fun navigateToColormathWebsite() {
js("window.location.href = 'https://github.com/ajalt/colormath'")
}
6 changes: 3 additions & 3 deletions website/src/wasmJsMain/kotlin/ColorPickerViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ class ColorPickerViewModel {
menuText = colorString
menuExpanded = false
val newColor = parseColorOrNull(colorString) ?: return
changeColor(newColor, skipMenu = true)
changeColor(newColor.convertTo(selectedColor.space), skipMenu = true)
}

private fun changeColor(
Expand Down Expand Up @@ -303,7 +303,7 @@ class ColorPickerViewModel {
var s = colorString.trim()
if ('(' in s && !s.endsWith(')')) s = "$s)"
return try {
Color.parse(s)
Color.parse(s, customColorSpaces = customColorSpaces)
} catch (e: IllegalArgumentException) {
null
}
Expand Down Expand Up @@ -342,7 +342,7 @@ private val menuSpacesToString = listOf<(Color) -> String>(
{ it.toAnsi16().formatCssString(customColorSpaces = customColorSpaces) },
{ it.toAnsi256().formatCssString(customColorSpaces = customColorSpaces) },
)
private val customColorSpaces = listOf(
private val customColorSpaces = mapOf(
"hsv" to HSV,
"luv" to LUV,
"lch-uv" to LCHuv,
Expand Down

0 comments on commit a2b0eda

Please sign in to comment.