Decaffeinate BrailleBuffer class

This commit is contained in:
Christian Paul 2017-11-24 02:01:05 -08:00
parent 9d9ef31f7b
commit d27e086758
2 changed files with 211 additions and 156 deletions

View File

@ -1,156 +0,0 @@
###
termap - Terminal Map Viewer
by Michael Strassburger <codepoet@cpan.org>
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]

211
src/BrailleBuffer.js Normal file
View File

@ -0,0 +1,211 @@
/*
termap - Terminal Map Viewer
by Michael Strassburger <codepoet@cpan.org>
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;