Skip to content

Commit

Permalink
feat(Color): further segmentation of Color
Browse files Browse the repository at this point in the history
  • Loading branch information
brekk committed Jul 13, 2024
1 parent d56866a commit f9ea037
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 125 deletions.
122 changes: 31 additions & 91 deletions src/Color.mad
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import type { Random } from "Random"

import { ge } from "Compare"
import Float from "Float"
import Integer from "Integer"
import { repeatWith } from "List"
import Math from "Math"
import R from "Random"
import Random from "Random"
import Term from "Terminal"

import { ANSI_BACKGROUND, ANSI_FOREGROUND, FULL_CHANNEL, HALF_CHANNEL } from "@/Color/Constants"
import { channelB, channelG, channelR } from "@/Color/Math"
import { colorFromSeed } from "@/Color/Random"



export alias Color a = #[a, a, a]
export BLACK = #[0, 0, 0]
export WHITE = #[FULL_CHANNEL, FULL_CHANNEL, FULL_CHANNEL]

// derive Comparable Color a

Expand All @@ -35,50 +37,12 @@ export toTupleInt = where {
}

export toRGB = (v) => {
r = hexR(v)
g = hexG(v)
b = hexB(v)
r = channelR(v)
g = channelG(v)
b = channelB(v)
return #[r, g, b]
}

toAnsi256 :: (Bits a, Comparable a, Number a) => a -> a -> a -> Float
export toAnsi256 = (r, g, b) => {
r4 = r >> 4
g4 = g >> 4
b4 = b >> 4
return if (r4 == g4 && g4 == b4) {
if (r < 8) {
16
} else if (r > 248) {
231
} else {
Math.round(((r - 8) / 247) * 24) + 232
}
} else {
16 + (36 * Math.round(r / 255 * 5)) + (6 * Math.round(g / 255 * 5)) + Math.round(b / 255 * 5)
}
}

MAX_OCTET = 255

colorFromSeed :: Random -> Color Integer
colorFromSeed = (seed) => {
pull = R.integer(0, MAX_OCTET)
r = pull(seed)
g = pull(seed)
b = pull(seed)
return #[r, g, b]
}

export genRGB = pipe(
R.generateFromString,
colorFromSeed,
)

//ESC[48;2;⟨r⟩;⟨g⟩;⟨b⟩ m Select RGB background color
COLOR_BACKGROUND = 48
//ESC[38;2;⟨r⟩;⟨g⟩;⟨b⟩ m Select RGB foreground color
COLOR_FOREGROUND = 38

toList :: Color a -> List a
export toList = where {
Expand All @@ -87,66 +51,42 @@ export toList = where {
}


export seededSeq = (before, str) => pipe(
genRGB,
toList,
mappend([before, 2]),
map(show),
)(str)
isMid = ge($, HALF_CHANNEL)

export rgbInvert = where {
#[r, g, b] =>
#[MAX_OCTET - r, MAX_OCTET - g, MAX_OCTET - b]
}

export seededSeqBg = seededSeq(COLOR_BACKGROUND)
export seededSeqFg = seededSeq(COLOR_FOREGROUND)


HALF_OCTET = 0x7f

isMid = ge($, HALF_OCTET)
mixer :: (Color a -> Color a -> Color b) -> Color a -> Color a -> Color b
export mixer = (mix, a, b) => mix(a, b)

seeded :: String -> String -> List String
export seeded = (seedPrefix, str) => pipe(
mappend(seedPrefix),
genRGB,
Random.generateFromString,
colorFromSeed,
(color) => {
black = pipe(
toTupleFloat,
isLuminous,
)(color)
return where(color) {
#[r, g, b] =>
map(show)([
COLOR_FOREGROUND,
2,
black ? 0 : MAX_OCTET,
black ? 0 : MAX_OCTET,
black ? 0 : MAX_OCTET,
COLOR_BACKGROUND,
2,
r,
g,
b,
])
do {
blackChannel = black ? 0 : FULL_CHANNEL
return map(show)([
ANSI_FOREGROUND,
2,
blackChannel,
blackChannel,
blackChannel,
ANSI_BACKGROUND,
2,
r,
g,
b,
])
}
}
},
)(str)

ansiFgBg :: String -> List String -> String
export ansiFgBg = (t, ansiSeq) => Term.ansiColor(ansiSeq, t)



hexR :: Integer -> Integer
hexR = (raw) => raw >> 16 & 0xff

hexG :: Integer -> Integer
hexG = (raw) => raw >> 8 & 0xff

hexB :: Integer -> Integer
hexB = (raw) => raw & 0xff

luminance :: Color Float -> Color Float
export luminance = where {
Expand All @@ -170,5 +110,5 @@ export contrastL = pipe(
contrastColor :: Color Float -> Color Float
export contrastColor = pipe(
contrastL,
(x) => x >= 0x7f ? #[0, 0, 0] : #[MAX_OCTET, MAX_OCTET, MAX_OCTET],
(x) => x >= HALF_CHANNEL ? WHITE : BLACK,
)
53 changes: 21 additions & 32 deletions src/Color.spec.mad
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,33 @@ import String from "String"
import { assertEquals, test } from "Test"

import Color from "@/Color"
import { toAnsi256 } from "@/Color/Math"
import { seededSeqBg, seededSeqFg } from "@/Color/Random"
import { report } from "@/Test"



test(
"toAnsi256",
() => do {
_ <- assertEquals(Color.toAnsi256(1, 0, 0), 16)
_ <- assertEquals(Color.toAnsi256(249, 0, 0), 196)
_ <- assertEquals(Color.toAnsi256(249, 250, 250), 231)
_ <- assertEquals(Color.toAnsi256(100, 100, 100), 241)
return assertEquals(Color.toAnsi256(244, 200, 2), 220)
_ <- assertEquals(toAnsi256(1, 0, 0), 16)
_ <- assertEquals(toAnsi256(249, 0, 0), 196)
_ <- assertEquals(toAnsi256(249, 250, 250), 231)
_ <- assertEquals(toAnsi256(100, 100, 100), 241)
return assertEquals(toAnsi256(244, 200, 2), 220)
},
)

test(
"seededSeqBg",
() => do {
_ <- assertEquals(Color.seededSeqFg("cool"), ["38", "2", "149", "50", "143"])
_ <- assertEquals(Color.seededSeqBg("cool"), ["48", "2", "149", "50", "143"])
_ <- assertEquals(Color.seededSeqBg("cool"), ["48", "2", "149", "50", "143"])
return assertEquals(Color.seededSeqBg("nice"), ["48", "2", "240", "101", "83"])
_ <- assertEquals(seededSeqFg("cool"), ["38", "2", "149", "50", "143"])
_ <- assertEquals(seededSeqBg("cool"), ["48", "2", "149", "50", "143"])
_ <- assertEquals(seededSeqBg("cool"), ["48", "2", "149", "50", "143"])
return assertEquals(seededSeqBg("nice"), ["48", "2", "240", "101", "83"])
},
)

test(
"genRGB",
() => do {
return assertEquals(Color.genRGB("cool"), #[149, 50, 143])
},
)
test(
"seeded",
() => {
Expand All @@ -41,21 +38,13 @@ test(
},
)

test(
"ansiFgBg",
() => do {
return pipe(
Color.ansiFgBg("cool"),
String.split("m"),
chain(String.split(";")),
map(String.toList),
(raw) => do {
_ <- assertEquals(List.slice(1, 4, raw), [['2'], ['4', '6'], ['2', '1', '4'], ['1']])
return assertEquals(
List.slice(6, 9, raw),
[['2'], ['2', '0', '9'], ['4', '1'], ['2', '5', '4']],
)
},
)(["38", "2", "46", "214", "1", "48", "2", "209", "41", "254"])
},
report(
Color.toRGB,
"toRGB",
[
#[0xffffff, Color.WHITE],
#[0x000000, Color.BLACK],
#[0xdeadbe, #[222, 173, 190]],
#[0x123456, #[18, 52, 86]],
],
)
8 changes: 8 additions & 0 deletions src/Color/Constants.mad
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export FULL_CHANNEL = 0xff
//ESC[48;2;⟨r⟩;⟨g⟩;⟨b⟩ m Select RGB background color
export ANSI_BACKGROUND = 48
//ESC[38;2;⟨r⟩;⟨g⟩;⟨b⟩ m Select RGB foreground color
export ANSI_FOREGROUND = 38


export HALF_CHANNEL = 0x7f
33 changes: 33 additions & 0 deletions src/Color/Math.mad
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Math from "Math"

import { FULL_CHANNEL } from "@/Color/Constants"



toAnsi256 :: (Bits a, Comparable a, Number a) => a -> a -> a -> Float
export toAnsi256 = (r, g, b) => {
r4 = r >> 4
g4 = g >> 4
b4 = b >> 4
return if (r4 == g4 && g4 == b4) {
if (r < 8) {
16
} else if (r > 248) {
231
} else {
Math.round(((r - 8) / 247) * 24) + 232
}
} else {
16 + (36 * Math.round(r / 255 * 5)) + (6 * Math.round(g / 255 * 5)) + Math.round(b / 255 * 5)
}
}


channelR :: Integer -> Integer
export channelR = (raw) => (raw >> 16) & 0xff

channelG :: Integer -> Integer
export channelG = (raw) => (raw >> 8) & 0xff

channelB :: Integer -> Integer
export channelB = (raw) => raw & 0xff
37 changes: 37 additions & 0 deletions src/Color/Random.mad
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { Random } from "Random"

import { generateFromSeed } from "Random"
import Random from "Random"

import { ANSI_BACKGROUND, ANSI_FOREGROUND, FULL_CHANNEL } from "@/Color/Constants"



colorFromSeed :: Random -> #[Integer, Integer, Integer]
export colorFromSeed = (seed) => {
pull = Random.integer(0, FULL_CHANNEL)
r = pull(seed)
g = pull(seed)
b = pull(seed)
return #[r, g, b]
}

export seededSeq = (before, str) => pipe(
Random.generateFromString,
colorFromSeed,
where {
#[r, g, b] =>
pipe(
mappend([before, 2]),
map(show),
)([r, g, b])
},
)(str)

export rgbInvert = where {
#[r, g, b] =>
#[FULL_CHANNEL - r, FULL_CHANNEL - g, FULL_CHANNEL - b]
}

export seededSeqBg = seededSeq(ANSI_BACKGROUND)
export seededSeqFg = seededSeq(ANSI_FOREGROUND)
5 changes: 3 additions & 2 deletions src/Processor.mad
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import type { Maybe } from "Maybe"
import type { Tag } from "@/Tag"

import { Just, Nothing } from "Maybe"
import Terminal from "Terminal"

import { ansiFgBg, seeded } from "@/Color"
import { seeded } from "@/Color"
import { fromString, matches, serialize } from "@/Tag"


Expand Down Expand Up @@ -35,7 +36,7 @@ styled :: String -> Trace String String -> List Tag -> String -> String -> Strin
export styled = (seedPrefix, run, raw) => parsed(
(t, x) => pipe(
seeded(seedPrefix),
ansiFgBg(" " ++ t ++ " "),
Terminal.ansiColor($, " " ++ t ++ " "),
(s) => ` ✿ ` ++ s ++ "\n ",
run($, x),
)(t),
Expand Down

0 comments on commit f9ea037

Please sign in to comment.