mirror of
https://github.com/rastapasta/mapscii.git
synced 2024-11-21 23:53:08 +01:00
Decaffeinate BrailleBuffer class
This commit is contained in:
parent
9d9ef31f7b
commit
d27e086758
@ -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
211
src/BrailleBuffer.js
Normal 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;
|
Loading…
Reference in New Issue
Block a user