diff --git a/src/BrailleBuffer.coffee b/src/BrailleBuffer.coffee deleted file mode 100644 index 643cb0e..0000000 --- a/src/BrailleBuffer.coffee +++ /dev/null @@ -1,156 +0,0 @@ -### - termap - Terminal Map Viewer - by Michael Strassburger - - Simple pixel to barille character mapper - - Implementation inspired by node-drawille (https://github.com/madbence/node-drawille) - * added color support - * added text label support - * general optimizations - - Will either be merged into node-drawille or become an own module at some point -### -stringWidth = require 'string-width' -config = require './config' -utils = require './utils' - -module.exports = class BrailleBuffer - brailleMap: [[0x1, 0x8],[0x2, 0x10],[0x4, 0x20],[0x40, 0x80]] - asciiMap: - # "▬": [2+32, 4+64] - # "¯": [1+16] - "▀": [1+2+16+32] - "▄": [4+8+64+128] - "■": [2+4+32+64] - "▌": [1+2+4+8] - "▐": [16+32+64+128] - # "▓": [1+4+32+128, 2+8+16+64] - "█": [255] - - pixelBuffer: null - charBuffer: null - foregroundBuffer: null - backgroundBuffer: null - - asciiToBraille: [] - - globalBackground: null - - termReset: "\x1B[39;49m" - - constructor: (@width, @height) -> - size = @width*@height/8 - @pixelBuffer = new Buffer size - @foregroundBuffer = new Buffer size - @backgroundBuffer = new Buffer size - - @_mapBraille() - @clear() - - clear: -> - @pixelBuffer.fill 0 - @charBuffer = [] - @foregroundBuffer.fill 0 - @backgroundBuffer.fill 0 - - setGlobalBackground: (@globalBackground) -> - - setBackground: (x, y, color) -> - return unless 0 <= x < @width and 0 <= y < @height - idx = @_project x, y - @backgroundBuffer[idx] = color - - setPixel: (x, y, color) -> - @_locate x, y, (idx, mask) => - @pixelBuffer[idx] |= mask - @foregroundBuffer[idx] = color - - unsetPixel: (x, y) -> - @_locate x, y, (idx, mask) => - @pixelBuffer[idx] &= ~mask - - _project: (x, y) -> - (x>>1) + (@width>>1)*(y>>2) - - _locate: (x, y, cb) -> - return unless 0 <= x < @width and 0 <= y < @height - idx = @_project x, y - mask = @brailleMap[y&3][x&1] - cb idx, mask - - _mapBraille: -> - @asciiToBraille = [" "] - - masks = [] - for char, bits of @asciiMap - continue unless bits instanceof Array - masks.push mask: mask, char: char for mask in bits - - for i in [1..255] - braille = (i&7) + ((i&56)<<1) + ((i&64)>>3) + (i&128) - - @asciiToBraille[i] = masks.reduce(((best, mask) -> - covered = utils.population(mask.mask&braille) - if not best or best.covered < covered - char: mask.char, covered: covered - else - best - ), undefined).char - - _termColor: (foreground, background) -> - background = background or @globalBackground - if foreground and background - "\x1B[38;5;#{foreground};48;5;#{background}m" - else if foreground - "\x1B[49;38;5;#{foreground}m" - else if background - "\x1B[39;48;5;#{background}m" - else - @termReset - - frame: -> - output = [] - currentColor = null - skip = 0 - - for y in [0...@height/4] - skip = 0 - - for x in [0...@width/2] - idx = y*@width/2 + x - - if idx and not x - output.push config.delimeter - - if currentColor isnt colorCode = @_termColor @foregroundBuffer[idx], @backgroundBuffer[idx] - output.push currentColor = colorCode - - output.push if char = @charBuffer[idx] - skip += stringWidth(char)-1 - if skip+x >= @width/2 - '' - else - char - else - if not skip - if config.useBraille - String.fromCharCode 0x2800+@pixelBuffer[idx] - else - @asciiToBraille[@pixelBuffer[idx]] - else - skip-- - '' - - output.push @termReset+config.delimeter - output.join '' - - setChar: (char, x, y, color) -> - return unless 0 <= x < @width and 0 <= y < @height - idx = @_project x, y - @charBuffer[idx] = char - @foregroundBuffer[idx] = color - - writeText: (text, x, y, color, center = true) -> - x -= text.length/2+1 if center - @setChar text.charAt(i), x+i*2, y, color for i in [0...text.length] diff --git a/src/BrailleBuffer.js b/src/BrailleBuffer.js new file mode 100644 index 0000000..6266030 --- /dev/null +++ b/src/BrailleBuffer.js @@ -0,0 +1,211 @@ +/* + termap - Terminal Map Viewer + by Michael Strassburger + + Simple pixel to barille character mapper + + Implementation inspired by node-drawille (https://github.com/madbence/node-drawille) + * added color support + * added text label support + * general optimizations + + Will either be merged into node-drawille or become an own module at some point +*/ +'use strict'; +const stringWidth = require('string-width'); +const config = require('./config'); +const utils = require('./utils'); + +const asciiMap = { + // '▬': [2+32, 4+64], + // '¯': [1+16], + '▀': [1+2+16+32], + '▄': [4+8+64+128], + '■': [2+4+32+64], + '▌': [1+2+4+8], + '▐': [16+32+64+128], + // '▓': [1+4+32+128, 2+8+16+64], + '█': [255], +}; +const termReset = '\x1B[39;49m'; + +class BrailleBuffer { + constructor(width, height) { + this.brailleMap = [[0x1, 0x8],[0x2, 0x10],[0x4, 0x20],[0x40, 0x80]] + + this.pixelBuffer = null; + this.charBuffer = null; + this.foregroundBuffer = null; + this.backgroundBuffer = null; + + this.asciiToBraille = []; + + this.globalBackground = null; + + this.width = width; + this.height = height; + + const size = width*height/8; + this.pixelBuffer = new Buffer(size); + this.foregroundBuffer = new Buffer(size); + this.backgroundBuffer = new Buffer(size); + + this._mapBraille(); + this.clear(); + } + + clear() { + this.pixelBuffer.fill(0); + this.charBuffer = []; + this.foregroundBuffer.fill(0); + this.backgroundBuffer.fill(0); + } + + setGlobalBackground(background) { + this.globalBackground = background; + } + + setBackground(x, y, color) { + if (0 <= x && x < this.width && 0 <= y && y < this.height) { + idx = this._project(x, y); + this.backgroundBuffer[idx] = color; + } + } + + setPixel(x, y, color) { + this._locate(x, y, (idx, mask) => { + this.pixelBuffer[idx] |= mask; + this.foregroundBuffer[idx] = color; + }); + } + + unsetPixel(x, y) { + this._locate(x, y, (idx, mask) => { + this.pixelBuffer[idx] &= ~mask; + }); + } + + _project(x, y) { + return (x>>1) + (this.width>>1)*(y>>2); + } + + _locate(x, y, cb) { + if (!((0 <= x && x < this.width) && (0 <= y && y < this.height))) { + return; + } + const idx = this._project(x, y); + const mask = this.brailleMap[y & 3][x & 1]; + return cb(idx, mask); + } + + _mapBraille() { + this.asciiToBraille = [' ']; + + const masks = []; + for (const char in asciiMap) { + const bits = asciiMap[char]; + if (!(bits instanceof Array)) continue; + for (const mask of bits) { + masks.push({ + mask: mask, + char: char, + }); + } + } + + //TODO Optimize this part + var i, k; + const results = []; + for (i = k = 1; k <= 255; i = ++k) { + const braille = (i & 7) + ((i & 56) << 1) + ((i & 64) >> 3) + (i & 128); + results.push(this.asciiToBraille[i] = masks.reduce((function(best, mask) { + const covered = utils.population(mask.mask & braille); + if (!best || best.covered < covered) { + return { + char: mask.char, + covered: covered, + }; + } else { + return best; + } + }), void 0).char); + } + return results; + } + + _termColor(foreground, background) { + background |= this.globalBackground; + if (foreground && background) { + return `\x1B[38;5;${foreground};48;5;${background}m`; + } else if (foreground) { + return `\x1B[49;38;5;${foreground}m`; + } else if (background) { + return `\x1B[39;48;5;${background}m`; + } else { + return termReset; + } + } + + frame() { + const output = []; + let currentColor = null; + let skip = 0; + + for (let y = 0; y < this.height/4; y++) { + skip = 0; + + for (let x = 0; x < this.width/2; x++) { + const idx = y*this.width/2 + x; + + if (idx && !x) { + output.push(config.delimeter); + } + + const colorCode = this._termColor(this.foregroundBuffer[idx], this.backgroundBuffer[idx]); + if (currentColor !== colorCode) { + output.push(currentColor = colorCode); + } + + const char = this.charBuffer[idx]; + if (char) { + skip += stringWidth(char)-1; + if (skip+x < this.width/2) { + output.push(char); + } + } else { + if (!skip) { + if (config.useBraille) { + output.push(String.fromCharCode(0x2800+this.pixelBuffer[idx])); + } else { + output.push(this.asciiToBraille[this.pixelBuffer[idx]]); + } + } else { + skip--; + } + } + } + } + + output.push(termReset+config.delimeter); + return output.join(''); + } + + setChar(char, x, y, color) { + if (0 <= x && x < this.width && 0 <= y && y < this.height) { + const idx = this._project(x, y); + this.charBuffer[idx] = char; + this.foregroundBuffer[idx] = color; + } + } + + writeText(text, x, y, color, center = true) { + if (center) { + x -= text.length/2+1; + } + for (let i = 0; i < text.length; i++) { + this.setChar(text.charAt(i), x+i*2, y, color); + } + } +} + +module.exports = BrailleBuffer;