aboutsummaryrefslogtreecommitdiff
path: root/chart.go
diff options
context:
space:
mode:
Diffstat (limited to 'chart.go')
-rw-r--r--chart.go409
1 files changed, 409 insertions, 0 deletions
diff --git a/chart.go b/chart.go
new file mode 100644
index 0000000..dd4ac10
--- /dev/null
+++ b/chart.go
@@ -0,0 +1,409 @@
+package main
+
+import (
+ "fmt"
+ "math"
+
+ "github.com/kutuluk/xterm-color-chart/color"
+ "github.com/nsf/termbox-go"
+)
+
+// обертка над termbox'ом
+type Screen struct {
+ Buffer []termbox.Cell
+ Width int
+ Height int
+}
+
+func (s *Screen) reScreen() {
+ s.Buffer = termbox.CellBuffer()
+ s.Width, s.Height = termbox.Size()
+}
+
+func (s *Screen) Init() error {
+ err := termbox.Init()
+ if err == nil {
+ s.reScreen()
+ }
+ return err
+}
+
+func (s *Screen) Flush() {
+ termbox.Flush()
+ s.reScreen()
+}
+
+func (s *Screen) Clear(fg, bg termbox.Attribute) {
+ termbox.Clear(fg, bg)
+ s.reScreen()
+}
+
+var screen Screen
+
+func termNative(c int) termbox.Attribute {
+ return termbox.Attribute(c + 1)
+}
+
+func printString(x, y int, s string) {
+ // TODO срабатывает перенос на следующую строку при достижении правого края
+ // и ошибка при выходе из границ буфера снизу
+ offsetX := 0
+ offsetY := 0
+ for _, char := range s {
+ if char == '\n' {
+ offsetX = 0
+ offsetY++
+ } else {
+ screen.Buffer[x+offsetX+(y+offsetY)*screen.Width].Ch = char
+ offsetX++
+ }
+ }
+}
+
+func printLine(x, y int, s string) {
+ printLineAttr(x, y, s, termbox.ColorWhite, background)
+}
+
+func printLineAttr(x, y int, s string, fg, bg termbox.Attribute) {
+ offsetX := 0
+ offsetY := 0
+ for _, char := range s {
+ if char == '\n' {
+ offsetX = 0
+ offsetY++
+ } else {
+ termbox.SetCell(x+offsetX, y+offsetY, char, fg, bg)
+ offsetX++
+ }
+ }
+}
+
+func DrawColor(x, y int, c int) {
+
+ Grayscale := []byte{
+ 16, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243,
+ 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 231,
+ }
+
+ for xx := 0; xx < 13; xx++ {
+ termbox.SetCell(x+xx, y, ' ', termbox.ColorBlack, termbox.Attribute(c))
+ }
+
+ // round(36 * (r * 5) + 6 * (g * 5) + (b * 5) + 16)
+ // https://gist.github.com/MicahElliott/719710
+
+ v := int(0.2125*float64(color.XtermRGBPalette[c].R) + 0.7154*float64(color.XtermRGBPalette[c].G) + 0.0721*float64(color.XtermRGBPalette[c].B))
+
+ fg := termbox.ColorRed
+ // if v < 128 {
+ // fg = termbox.ColorWhite
+ //fg = termbox.Attribute(231)
+ delta := 64
+ center := 128
+ if (v > (center - delta)) && (v <= center) {
+ fg = termbox.Attribute(Grayscale[25])
+ } else if (v > center) && (v < (center + delta)) {
+ fg = termbox.Attribute(Grayscale[0])
+ } else {
+ //fg = termbox.Attribute(Grayscale[12])
+ fg = termbox.Attribute(Grayscale[25-int(v/11)])
+ }
+
+ printLineAttr(x+1, y, fmt.Sprintf("%03d #%s", int(c), color.XtermRGBPalette[c]), fg, termbox.Attribute(c))
+}
+
+func DrawBox(left, top, d, l int) {
+ var approxer string
+ switch currentComparer {
+ case 0:
+ approxer = "CIE76"
+ case 1:
+ approxer = "CIE94"
+ case 2:
+ approxer = "CIE2000"
+ }
+
+ r := d / 2
+ for y := 0; y < d; y++ {
+ for x := 0; x < d; x++ {
+ a := 100.0 * float64(x-r) / float64(r)
+ b := -100.0 * float64(y-r) / float64(r)
+ _, c := color.Approximate(color.LabColor{float64(l), a, b}, comparers[currentComparer])
+ //termbox.SetCell(x*2+left, y+top, ' ', background, termbox.Attribute(color))
+ //termbox.SetCell(x*2+left+1, y+top, ' ', background, termbox.Attribute(color))
+ fg := int(background)
+ if y == 1 || y == 2 || y == d-2 {
+ fgLab := color.XtermRGBPalette[c-17].Lab()
+ if l > 50 {
+ fgLab.L -= 50
+ //fgLab.L = 20
+ } else {
+ fgLab.L += 50
+ //fgLab.L = 70
+ }
+ _, fg = color.Approximate(fgLab, color.DeltaCIE2000{})
+ }
+ termbox.SetCell(x*2+left, y+top, ' ', termbox.Attribute(fg), termbox.Attribute(c))
+ termbox.SetCell(x*2+left+1, y+top, ' ', termbox.Attribute(fg), termbox.Attribute(c))
+ }
+
+ }
+
+ labLabel := "CIE L*a*b color space"
+ if len(labLabel)+4 < d*2 {
+ printString(left+2, top+1, labLabel)
+ printString(left+2, top+2, fmt.Sprintf("Lightness = %d%%", l))
+ }
+ //printString(left+2, top+4, fmt.Sprintln("Press Up/Down key for change"))
+
+ approxerLabel := fmt.Sprintf("Approximate by %s algorithm", approxer)
+ if len(approxerLabel)+4 <= d*2 {
+ printString(left+2, top+d-2, approxerLabel)
+ }
+ //printString(left+2, top+7, fmt.Sprintln("Press Left/Right key for change"))
+}
+
+func DrawCircle(left, top, r int, l float64) {
+
+ var ch [2]rune
+ point := func(xb, yb int) {
+ xl := xb / r
+ yl := yb / r
+ distance, color := color.Approximate(color.LChColor{l, math.Sqrt(float64(xl*xl) + float64(yl*yl)), 180.0 * math.Atan2(float64(xl), float64(yl))}, comparers[currentComparer])
+ //distance, color := color.Palette(color.LabColor{l, float64(xb * 120.0 / r), float64(-yb * 120.0 / r)}, color.DeltaCIE94{})
+ if distance < 50 {
+ ch[0] = ' '
+ ch[1] = ' '
+ } else {
+ //ch[0] = '¤'
+ //ch[1] = '¤'
+ ch[0] = '('
+ ch[1] = ')'
+ }
+ termbox.SetCell((xb+r)*2+left, yb+r+top, ch[0], termbox.Attribute(0), termbox.Attribute(color))
+ termbox.SetCell((xb+r)*2+left+1, yb+r+top, ch[1], termbox.Attribute(0), termbox.Attribute(color))
+ }
+
+ // алгоритм Брезенхэма
+ line := func(x, y1, y2 int) {
+ for yf := y1; yf <= y2; yf++ {
+ point(x, yf)
+ }
+ }
+
+ x := 0
+ y := r
+ d := 3 - 2*r
+ for x <= y {
+ line(x, -y, y)
+ line(-x, -y, y)
+ line(y, -x, x)
+ line(-y, -x, x)
+ if d < 0 {
+ d += 4*x + 6
+ } else {
+ d += 4*(x-y) + 10
+ y--
+ }
+ x++
+ }
+}
+
+func DrawColorCell(x, y, c int) {
+ termbox.SetCell(x, y, ' ', termbox.Attribute(1), termbox.Attribute(c))
+ termbox.SetCell(x+1, y, ' ', termbox.Attribute(1), termbox.Attribute(c))
+}
+
+// left, top - левый верхний угол отрисовки палитры
+// row - количество кубов 6x6 по горизонтали
+// num - признак отрисовки номеров цветов
+func DrawPalette(left, top, row int, num bool) {
+ var numOffset int
+ if num {
+ numOffset = 4
+ }
+
+ //bh := [2]termbox.Attribute{237, background}
+ bh := [2]termbox.Attribute{236, background}
+ //th := [2]termbox.Attribute{232, 241}
+ th := [2]termbox.Attribute{243, 243}
+
+ cc := 1
+
+ printLineAttr(left, top, fmt.Sprintln("16 system colors"), text, background)
+
+ printLineAttr(left, top+2, fmt.Sprintf("%3d ", cc-1), th[1], bh[1])
+ printLineAttr(left+numOffset+16*2, top+2, fmt.Sprintf(" %-3d", cc+14), th[1], bh[1])
+
+ for x := 0; x < 16; x++ {
+ DrawColorCell(left+numOffset+x*2, top+2, cc)
+ cc++
+ }
+
+ padding := 1
+ if num {
+ // padding = 0
+ }
+
+ printLineAttr(left, top+4, fmt.Sprintln("216 colors of color cube 6x6x6"), text, background)
+ col := int(6 / row) // ширина
+ for l := 0; l < row; l++ { // l - вертикальный счетчик
+ for k := 0; k < col; k++ { // k - горизонтальный счетчик
+ for y := 0; y < 6; y++ {
+ h := y % 2
+ if h != 0 {
+ h = 1
+ }
+ printLineAttr(left+k*numOffset*2+k*(6+padding)*2, top+l*(6+padding)+y+6, fmt.Sprintf("%3d ", cc-1), th[h], bh[h])
+ printLineAttr(left+k*numOffset*2+k*(6+padding)*2+16, top+l*(6+padding)+y+6, fmt.Sprintf(" %-3d", cc+4), th[h], bh[h])
+ for x := 0; x < 6; x++ {
+ DrawColorCell(left+numOffset+k*(6+padding)*2+k*numOffset*2+x*2, top+l*(6+padding)+y+6, cc)
+ cc++
+ }
+ }
+ }
+ }
+
+ printLineAttr(left, top+row*7+6, fmt.Sprintln("24 grayscale colors"), text, background)
+ for y := 0; y < 2; y++ {
+ for x := 0; x < 12; x++ {
+ DrawColorCell(left+numOffset+x*2, top+row*7+y+8, cc)
+ cc++
+ }
+ }
+
+ printLineAttr(left, top+row*7+8, fmt.Sprintf("%3d ", 232), th[1], bh[1])
+ printLineAttr(left+28, top+row*7+8, fmt.Sprintf(" %-3d", 243), th[1], bh[1])
+ printLineAttr(left, top+row*7+9, fmt.Sprintf("%3d ", 244), th[1], bh[1])
+ printLineAttr(left+28, top+row*7+9, fmt.Sprintf(" %-3d ", 255), th[1], bh[1])
+}
+
+func Fill(x1, y1, x2, y2 int, fg, bg termbox.Attribute) {
+ for y := y1; y <= y2; y++ {
+ for x := x1; x <= x2; x++ {
+ termbox.SetCell(x, y, ' ', fg, bg)
+ }
+ }
+}
+
+var (
+ text, background termbox.Attribute
+ lightness int
+ helpVisible bool
+)
+
+func DrawChart() {
+ screen.Clear(text, background)
+ //Fill(0, 0, screen.Width-1, screen.Height-1, text, background)
+
+ Fill(0, 0, screen.Width-1, 0, background, background+8)
+ printString(2, 0, "XTerm 256 color palette chart")
+ printString(screen.Width-19, 0, "F1 Help F10 Exit")
+ printLineAttr(screen.Width-19, 0, "F1", background+20, background+8)
+ printLineAttr(screen.Width-10, 0, "F10", background+20, background+8)
+
+ paletteSize := 42
+
+ boxSize := screen.Height - 3
+ w := int((screen.Width - paletteSize - 6) / 2)
+ if w < boxSize {
+ boxSize = w
+ }
+
+ DrawBox(2, 2, boxSize, lightness)
+ // http://snag.gy/XMro8.jpg - картинка для сравнения
+
+ helpText := []string{
+ "Control keys:",
+ "",
+ "F1 - show/hide this help",
+ "Up/Down - change lightness",
+ "Left/Right - change approximation method",
+ "",
+ "CIE76 - fastest",
+ "CIE94 - middle",
+ "CIE2000 - slowest",
+ "",
+ "",
+ }
+
+ xc := boxSize*2 + 4
+ if xc < 2 {
+ xc = 2
+ }
+ yc := 2
+
+ if helpVisible {
+ for i, line := range helpText {
+ printString(xc, yc+i, line)
+ }
+ } else {
+ DrawPalette(xc, yc, 3, true)
+ }
+
+ screen.Flush()
+}
+
+var comparers []color.Comparer
+var currentComparer int
+
+func main() {
+
+ err := screen.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer termbox.Close()
+ termbox.SetOutputMode(termbox.Output256)
+ //termbox.SetInputMode(termbox.InputEsc)
+
+ background = termNative(234)
+ text = background + 12
+ lightness = 100
+
+ comparers = append(comparers, color.DeltaCIE76{}, color.DeltaCIE94{}, color.DeltaCIE2000{})
+
+ //size := int(h/2 - 3)
+ //DrawCircle(2, 1, size, 1.0)
+
+ DrawChart()
+
+ for loop := true; loop; {
+ switch ev := termbox.PollEvent(); ev.Type {
+ case termbox.EventKey:
+ switch ev.Key {
+ case termbox.KeyCtrlQ, termbox.KeyF10:
+ loop = false
+ case termbox.KeyCtrlH, termbox.KeyF1:
+ helpVisible = !helpVisible
+ DrawChart()
+ case termbox.KeyArrowUp:
+ if lightness < 100 {
+ lightness = lightness + 10.0
+ DrawChart()
+ }
+ case termbox.KeyArrowDown:
+ if lightness > 0 {
+ lightness = lightness - 10.0
+ DrawChart()
+ }
+ case termbox.KeyArrowLeft:
+ if currentComparer > 0 {
+ currentComparer--
+ } else {
+ currentComparer = len(comparers) - 1
+ }
+ DrawChart()
+ case termbox.KeyArrowRight:
+ if currentComparer < 2 {
+ currentComparer++
+ } else {
+ currentComparer = 0
+ }
+ DrawChart()
+ }
+ case termbox.EventResize:
+ DrawChart()
+ }
+ }
+}