mirror of
https://github.com/ocogeclub/ocoge.git
synced 2024-11-25 08:59:49 +00:00
669 lines
19 KiB
JavaScript
669 lines
19 KiB
JavaScript
var Oled = function (rg, opts) {
|
|
|
|
this.HEIGHT = opts.height || 32;
|
|
this.WIDTH = opts.width || 128;
|
|
this.ADDRESS = opts.address || 0x3C;
|
|
this.BUS = opts.bus || 1;
|
|
this.PROTOCOL = 'I2C';
|
|
this.LINESPACING = typeof opts.linespacing !== 'undefined' ? opts.linespacing : 1;
|
|
this.LETTERSPACING = typeof opts.letterspacing !== 'undefined' ? opts.letterspacing : 1;
|
|
|
|
|
|
// create command buffers
|
|
this.DISPLAY_OFF = 0xAE;
|
|
this.DISPLAY_ON = 0xAF;
|
|
this.SET_DISPLAY_CLOCK_DIV = 0xD5;
|
|
this.SET_MULTIPLEX = 0xA8;
|
|
this.SET_DISPLAY_OFFSET = 0xD3;
|
|
this.SET_START_LINE = 0x00;
|
|
this.CHARGE_PUMP = 0x8D;
|
|
this.EXTERNAL_VCC = false;
|
|
this.MEMORY_MODE = 0x20;
|
|
this.SEG_REMAP = 0xA1; // using 0xA0 will flip screen
|
|
this.COM_SCAN_DEC = 0xC8;
|
|
this.COM_SCAN_INC = 0xC0;
|
|
this.SET_COM_PINS = 0xDA;
|
|
this.SET_CONTRAST = 0x81;
|
|
this.SET_PRECHARGE = 0xd9;
|
|
this.SET_VCOM_DETECT = 0xDB;
|
|
this.DISPLAY_ALL_ON_RESUME = 0xA4;
|
|
this.NORMAL_DISPLAY = 0xA6;
|
|
this.COLUMN_ADDR = 0x21;
|
|
this.PAGE_ADDR = 0x22;
|
|
this.INVERT_DISPLAY = 0xA7;
|
|
this.ACTIVATE_SCROLL = 0x2F;
|
|
this.DEACTIVATE_SCROLL = 0x2E;
|
|
this.SET_VERTICAL_SCROLL_AREA = 0xA3;
|
|
this.RIGHT_HORIZONTAL_SCROLL = 0x26;
|
|
this.LEFT_HORIZONTAL_SCROLL = 0x27;
|
|
this.VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29;
|
|
this.VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A;
|
|
|
|
this.cursor_x = 0;
|
|
this.cursor_y = 0;
|
|
|
|
// new blank buffer
|
|
this.buffer = Buffer.alloc((this.WIDTH * this.HEIGHT) / 8);
|
|
this.buffer.fill(0x00);
|
|
|
|
this.dirtyBytes = [];
|
|
|
|
var config = {
|
|
'128x32': {
|
|
'multiplex': 0x1F,
|
|
'compins': 0x02,
|
|
'coloffset': 0
|
|
},
|
|
'128x64': {
|
|
'multiplex': 0x3F,
|
|
'compins': 0x12,
|
|
'coloffset': 0
|
|
},
|
|
'96x16': {
|
|
'multiplex': 0x0F,
|
|
'compins': 0x2,
|
|
'coloffset': 0,
|
|
}
|
|
};
|
|
|
|
// Setup i2c
|
|
this.rg = rg;
|
|
this.rg.rgpio_sbc_sync();
|
|
this.i2c_hand = this.rg.i2c_open_sync(this.BUS, this.ADDRESS);
|
|
// console.log(`this.i2c_hand = ${this.i2c_hand}`);
|
|
|
|
var screenSize = this.WIDTH + 'x' + this.HEIGHT;
|
|
this.screenConfig = config[screenSize];
|
|
|
|
this._initialise();
|
|
}
|
|
|
|
Oled.prototype._initialise = function () {
|
|
|
|
// sequence of bytes to initialise with
|
|
var initSeq = [
|
|
this.DISPLAY_OFF,
|
|
this.SET_DISPLAY_CLOCK_DIV, 0x80,
|
|
this.SET_MULTIPLEX, this.screenConfig.multiplex, // set the last value dynamically based on screen size requirement
|
|
this.SET_DISPLAY_OFFSET, 0x00, // sets offset pro to 0
|
|
this.SET_START_LINE,
|
|
this.CHARGE_PUMP, 0x14, // charge pump val
|
|
this.MEMORY_MODE, 0x00, // 0x0 act like ks0108
|
|
this.SEG_REMAP, // screen orientation
|
|
this.COM_SCAN_DEC, // screen orientation change to INC to flip
|
|
this.SET_COM_PINS, this.screenConfig.compins, // com pins val sets dynamically to match each screen size requirement
|
|
this.SET_CONTRAST, 0x8F, // contrast val
|
|
this.SET_PRECHARGE, 0xF1, // precharge val
|
|
this.SET_VCOM_DETECT, 0x40, // vcom detect
|
|
this.DISPLAY_ALL_ON_RESUME,
|
|
this.NORMAL_DISPLAY,
|
|
this.DISPLAY_ON
|
|
];
|
|
|
|
var i, initSeqLen = initSeq.length;
|
|
|
|
// write init seq commands
|
|
for (i = 0; i < initSeqLen; i++) {
|
|
this._transfer('cmd', initSeq[i]);
|
|
}
|
|
}
|
|
|
|
// writes both commands and data buffers to this device
|
|
Oled.prototype._transfer = function (type, val, fn) {
|
|
var control;
|
|
if (type === 'data') {
|
|
control = 0x40;
|
|
} else if (type === 'cmd') {
|
|
control = 0x00;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
var bufferForSend;
|
|
bufferForSend = Buffer.from([control, val])
|
|
|
|
// send control and actual val
|
|
this.rg.i2c_write_device_sync(this.i2c_hand, bufferForSend, 2);
|
|
if (fn) {
|
|
fn();
|
|
}
|
|
}
|
|
|
|
// read a byte from the oled
|
|
Oled.prototype._readI2C = function (fn) {
|
|
var data = this.rg.i2c_read_byte_sync(this.i2c_hand);
|
|
fn(data);
|
|
}
|
|
|
|
// sometimes the oled gets a bit busy with lots of bytes.
|
|
// Read the response byte to see if this is the case
|
|
Oled.prototype._waitUntilReady = function (callback) {
|
|
var done,
|
|
oled = this;
|
|
|
|
function tick(callback) {
|
|
oled._readI2C(function (byte) {
|
|
// read the busy byte in the response
|
|
busy = byte >> 7 & 1;
|
|
if (!busy) {
|
|
// if not busy, it's ready for callback
|
|
callback();
|
|
} else {
|
|
setTimeout(function () { tick(callback) }, 0);
|
|
}
|
|
});
|
|
};
|
|
|
|
setTimeout(function () { tick(callback) }, 0);
|
|
}
|
|
|
|
// set starting position of a text string on the oled
|
|
Oled.prototype.setCursor = function (x, y) {
|
|
this.cursor_x = x;
|
|
this.cursor_y = y;
|
|
}
|
|
|
|
// write text to the oled
|
|
Oled.prototype.writeString = function (font, size, string, color, wrap, sync) {
|
|
var immed = (typeof sync === 'undefined') ? true : sync;
|
|
var wordArr = string.split(' '),
|
|
len = wordArr.length,
|
|
// start x offset at cursor pos
|
|
offset = this.cursor_x,
|
|
padding = 0;
|
|
|
|
// loop through words
|
|
for (var w = 0; w < len; w += 1) {
|
|
// put the word space back in for all in between words or empty words
|
|
if (w < len - 1 || !wordArr[w].length) {
|
|
wordArr[w] += ' ';
|
|
}
|
|
var stringArr = wordArr[w].split(''),
|
|
slen = stringArr.length,
|
|
compare = (font.width * size * slen) + (size * (len - 1));
|
|
|
|
// wrap words if necessary
|
|
if (wrap && len > 1 && w > 0 && (offset >= (this.WIDTH - compare))) {
|
|
offset = 0;
|
|
|
|
this.cursor_y += (font.height * size) + this.LINESPACING;
|
|
this.setCursor(offset, this.cursor_y);
|
|
}
|
|
|
|
// loop through the array of each char to draw
|
|
for (var i = 0; i < slen; i += 1) {
|
|
if (stringArr[i] === '\n') {
|
|
offset = 0;
|
|
this.cursor_y += (font.height * size) + this.LINESPACING;
|
|
this.setCursor(offset, this.cursor_y);
|
|
}
|
|
else {
|
|
// look up the position of the char, pull out the buffer slice
|
|
var charBuf = this._findCharBuf(font, stringArr[i]);
|
|
// read the bits in the bytes that make up the char
|
|
var charBytes = this._readCharBytes(charBuf, font.height);
|
|
// draw the entire character
|
|
this._drawChar(charBytes, font.height, size, false);
|
|
|
|
// calc new x position for the next char, add a touch of padding too if it's a non space char
|
|
//padding = (stringArr[i] === ' ') ? 0 : this.LETTERSPACING;
|
|
offset += (font.width * size) + this.LETTERSPACING;// padding;
|
|
|
|
// wrap letters if necessary
|
|
if (wrap && (offset >= (this.WIDTH - font.width - this.LETTERSPACING))) {
|
|
offset = 0;
|
|
this.cursor_y += (font.height * size) + this.LINESPACING;
|
|
}
|
|
// set the 'cursor' for the next char to be drawn, then loop again for next char
|
|
this.setCursor(offset, this.cursor_y);
|
|
}
|
|
}
|
|
}
|
|
if (immed) {
|
|
this._updateDirtyBytes(this.dirtyBytes);
|
|
}
|
|
}
|
|
|
|
// draw an individual character to the screen
|
|
Oled.prototype._drawChar = function (byteArray, charHeight, size, sync) {
|
|
// take your positions...
|
|
var x = this.cursor_x,
|
|
y = this.cursor_y;
|
|
|
|
// loop through the byte array containing the hexes for the char
|
|
for (var i = 0; i < byteArray.length; i += 1) {
|
|
for (var j = 0; j < charHeight; j += 1) {
|
|
// pull color out
|
|
var color = byteArray[i][j],
|
|
xpos, ypos;
|
|
// standard font size
|
|
if (size === 1) {
|
|
xpos = x + i;
|
|
ypos = y + j;
|
|
this.drawPixel([xpos, ypos, color], false);
|
|
} else {
|
|
// MATH! Calculating pixel size multiplier to primitively scale the font
|
|
xpos = x + (i * size);
|
|
ypos = y + (j * size);
|
|
this.fillRect(xpos, ypos, size, size, color, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// get character bytes from the supplied font object in order to send to framebuffer
|
|
Oled.prototype._readCharBytes = function (byteArray, charHeight) {
|
|
var bitArr = [],
|
|
bitCharArr = [];
|
|
// loop through each byte supplied for a char
|
|
for (var i = 0; i < byteArray.length; i += 1) {
|
|
// set current byte
|
|
var byte = byteArray[i];
|
|
// read each byte
|
|
for (var j = 0; j < charHeight; j += 1) {
|
|
// shift bits right until all are read
|
|
var bit = byte >> j & 1;
|
|
bitArr.push(bit);
|
|
}
|
|
// push to array containing flattened bit sequence
|
|
bitCharArr.push(bitArr);
|
|
// clear bits for next byte
|
|
bitArr = [];
|
|
}
|
|
return bitCharArr;
|
|
}
|
|
|
|
// find where the character exists within the font object
|
|
Oled.prototype._findCharBuf = function (font, c) {
|
|
// use the lookup array as a ref to find where the current char bytes start
|
|
var cBufPos = font.lookup.indexOf(c) * font.width;
|
|
// slice just the current char's bytes out of the fontData array and return
|
|
var cBuf = font.fontData.slice(cBufPos, cBufPos + font.width);
|
|
return cBuf;
|
|
}
|
|
|
|
// send the entire framebuffer to the oled
|
|
Oled.prototype.update = function () {
|
|
// wait for oled to be ready
|
|
this._waitUntilReady(function () {
|
|
// set the start and endbyte locations for oled display update
|
|
var displaySeq = [
|
|
this.COLUMN_ADDR,
|
|
this.screenConfig.coloffset,
|
|
this.screenConfig.coloffset + this.WIDTH - 1, // column start and end address
|
|
this.PAGE_ADDR, 0, (this.HEIGHT / 8) - 1 // page start and end address
|
|
];
|
|
|
|
var displaySeqLen = displaySeq.length,
|
|
bufferLen = this.buffer.length,
|
|
i, v;
|
|
|
|
// send intro seq
|
|
for (i = 0; i < displaySeqLen; i += 1) {
|
|
this._transfer('cmd', displaySeq[i]);
|
|
}
|
|
|
|
// write buffer data
|
|
var bufferToSend = Buffer.concat([Buffer.from([0x40]), this.buffer]);
|
|
this.rg.i2c_write_device_sync(this.i2c_hand, bufferToSend, bufferToSend.length);
|
|
|
|
}.bind(this));
|
|
}
|
|
|
|
// send dim display command to oled
|
|
Oled.prototype.dimDisplay = function (bool) {
|
|
var contrast;
|
|
|
|
if (bool) {
|
|
contrast = 0; // Dimmed display
|
|
} else {
|
|
contrast = 0xCF; // Bright display
|
|
}
|
|
|
|
this._transfer('cmd', this.SET_CONTRAST);
|
|
this._transfer('cmd', contrast);
|
|
}
|
|
|
|
// turn oled off
|
|
Oled.prototype.turnOffDisplay = function () {
|
|
this._transfer('cmd', this.DISPLAY_OFF);
|
|
}
|
|
|
|
// turn oled on
|
|
Oled.prototype.turnOnDisplay = function () {
|
|
this._transfer('cmd', this.DISPLAY_ON);
|
|
}
|
|
|
|
// clear all pixels currently on the display
|
|
Oled.prototype.clearDisplay = function (sync) {
|
|
var immed = (typeof sync === 'undefined') ? true : sync;
|
|
// write off pixels
|
|
this.buffer.fill(0x00);
|
|
if (immed) {
|
|
this.update();
|
|
}
|
|
|
|
}
|
|
|
|
// invert pixels on oled
|
|
Oled.prototype.invertDisplay = function (bool) {
|
|
if (bool) {
|
|
this._transfer('cmd', this.INVERT_DISPLAY); // inverted
|
|
} else {
|
|
this._transfer('cmd', this.NORMAL_DISPLAY); // non inverted
|
|
}
|
|
}
|
|
|
|
// draw an RGBA image at the specified coordinates
|
|
Oled.prototype.drawRGBAImage = function (image, dx, dy, sync) {
|
|
var immed = (typeof sync === 'undefined') ? true : sync;
|
|
// translate image data to buffer
|
|
var x, y, dataIndex, buffIndex, buffByte, bit, pixelByte;
|
|
var dyp = this.WIDTH * Math.floor(dy / 8); // calc once
|
|
var dxyp = dyp + dx;
|
|
for (x = 0; x < image.width; x++) {
|
|
var dxx = dx + x;
|
|
if (dxx < 0 || dxx >= this.WIDTH) {
|
|
// negative, off the screen
|
|
continue;
|
|
}
|
|
// start buffer index for image column
|
|
buffIndex = x + dxyp;
|
|
buffByte = this.buffer[buffIndex];
|
|
for (y = 0; y < image.height; y++) {
|
|
var dyy = dy + y; // calc once
|
|
if (dyy < 0 || dyy >= this.HEIGHT) {
|
|
// negative, off the screen
|
|
continue;
|
|
}
|
|
var dyyp = Math.floor(dyy / 8); // calc once
|
|
|
|
// check if start of buffer page
|
|
if (!(dyy % 8)) {
|
|
// check if we need to save previous byte
|
|
if ((x || y) && buffByte !== this.buffer[buffIndex]) {
|
|
// save current byte and get next buffer byte
|
|
this.buffer[buffIndex] = buffByte;
|
|
this.dirtyBytes.push(buffIndex);
|
|
}
|
|
// new buffer page
|
|
buffIndex = dx + x + this.WIDTH * dyyp;
|
|
buffByte = this.buffer[buffIndex];
|
|
}
|
|
|
|
// process pixel into buffer byte
|
|
dataIndex = (image.width * y + x) << 2; // 4 bytes per pixel (RGBA)
|
|
if (!image.data[dataIndex + 3]) {
|
|
// transparent, continue to next pixel
|
|
continue;
|
|
}
|
|
|
|
pixelByte = 0x01 << (dyy - 8 * dyyp);
|
|
bit = image.data[dataIndex] || image.data[dataIndex + 1] || image.data[dataIndex + 2];
|
|
if (bit) {
|
|
buffByte |= pixelByte;
|
|
}
|
|
else {
|
|
buffByte &= ~pixelByte;
|
|
}
|
|
}
|
|
if ((x || y) && buffByte !== this.buffer[buffIndex]) {
|
|
// save current byte
|
|
this.buffer[buffIndex] = buffByte;
|
|
this.dirtyBytes.push(buffIndex);
|
|
}
|
|
}
|
|
|
|
if (immed) {
|
|
this._updateDirtyBytes(this.dirtyBytes);
|
|
}
|
|
}
|
|
|
|
// draw an image pixel array on the screen
|
|
Oled.prototype.drawBitmap = function (pixels, sync) {
|
|
var immed = (typeof sync === 'undefined') ? true : sync;
|
|
var x, y,
|
|
pixelArray = [];
|
|
|
|
for (var i = 0; i < pixels.length; i++) {
|
|
x = Math.floor(i % this.WIDTH);
|
|
y = Math.floor(i / this.WIDTH);
|
|
|
|
this.drawPixel([x, y, pixels[i]], false);
|
|
}
|
|
|
|
if (immed) {
|
|
this._updateDirtyBytes(this.dirtyBytes);
|
|
}
|
|
}
|
|
|
|
// draw one or many pixels on oled
|
|
Oled.prototype.drawPixel = function (pixels, sync) {
|
|
var immed = (typeof sync === 'undefined') ? true : sync;
|
|
|
|
// handle lazy single pixel case
|
|
if (typeof pixels[0] !== 'object') pixels = [pixels];
|
|
|
|
pixels.forEach(function (el) {
|
|
// return if the pixel is out of range
|
|
var x = el[0], y = el[1], color = el[2];
|
|
if (x >= this.WIDTH || y >= this.HEIGHT) return;
|
|
|
|
// thanks, Martin Richards.
|
|
// I wanna can this, this tool is for devs who get 0 indexes
|
|
//x -= 1; y -=1;
|
|
var byte = 0,
|
|
page = Math.floor(y / 8),
|
|
pageShift = 0x01 << (y - 8 * page);
|
|
|
|
// is the pixel on the first row of the page?
|
|
(page == 0) ? byte = x : byte = x + (this.WIDTH * page);
|
|
|
|
// colors! Well, monochrome.
|
|
if (color === 'BLACK' || color === 0) {
|
|
this.buffer[byte] &= ~pageShift;
|
|
}
|
|
if (color === 'WHITE' || color > 0) {
|
|
this.buffer[byte] |= pageShift;
|
|
}
|
|
|
|
// push byte to dirty if not already there
|
|
if (this.dirtyBytes.indexOf(byte) === -1) {
|
|
this.dirtyBytes.push(byte);
|
|
}
|
|
|
|
}, this);
|
|
|
|
if (immed) {
|
|
this._updateDirtyBytes(this.dirtyBytes);
|
|
}
|
|
}
|
|
|
|
// looks at dirty bytes, and sends the updated bytes to the display
|
|
Oled.prototype._updateDirtyBytes = function (byteArray) {
|
|
var blen = byteArray.length, i,
|
|
displaySeq = [];
|
|
|
|
// check to see if this will even save time
|
|
if (blen > (this.buffer.length / 7)) {
|
|
// just call regular update at this stage, saves on bytes sent
|
|
this.update();
|
|
// now that all bytes are synced, reset dirty state
|
|
this.dirtyBytes = [];
|
|
|
|
} else {
|
|
|
|
this._waitUntilReady(function () {
|
|
// iterate through dirty bytes
|
|
for (var i = 0; i < blen; i += 1) {
|
|
|
|
var byte = byteArray[i];
|
|
var page = Math.floor(byte / this.WIDTH);
|
|
var col = Math.floor(byte % this.WIDTH);
|
|
|
|
var displaySeq = [
|
|
this.COLUMN_ADDR, col, col, // column start and end address
|
|
this.PAGE_ADDR, page, page // page start and end address
|
|
];
|
|
|
|
var displaySeqLen = displaySeq.length, v;
|
|
|
|
// send intro seq
|
|
for (v = 0; v < displaySeqLen; v += 1) {
|
|
this._transfer('cmd', displaySeq[v]);
|
|
}
|
|
// send byte, then move on to next byte
|
|
this._transfer('data', this.buffer[byte]);
|
|
this.buffer[byte];
|
|
}
|
|
}.bind(this));
|
|
}
|
|
// now that all bytes are synced, reset dirty state
|
|
this.dirtyBytes = [];
|
|
}
|
|
|
|
// using Bresenham's line algorithm
|
|
Oled.prototype.drawLine = function (x0, y0, x1, y1, color, sync) {
|
|
var immed = (typeof sync === 'undefined') ? true : sync;
|
|
|
|
var dx = Math.abs(x1 - x0), sx = x0 < x1 ? 1 : -1,
|
|
dy = Math.abs(y1 - y0), sy = y0 < y1 ? 1 : -1,
|
|
err = (dx > dy ? dx : -dy) / 2;
|
|
|
|
while (true) {
|
|
this.drawPixel([x0, y0, color], false);
|
|
|
|
if (x0 === x1 && y0 === y1) break;
|
|
|
|
var e2 = err;
|
|
|
|
if (e2 > -dx) { err -= dy; x0 += sx; }
|
|
if (e2 < dy) { err += dx; y0 += sy; }
|
|
}
|
|
|
|
if (immed) {
|
|
this._updateDirtyBytes(this.dirtyBytes);
|
|
}
|
|
}
|
|
|
|
// draw a filled rectangle on the oled
|
|
Oled.prototype.fillRect = function (x, y, w, h, color, sync) {
|
|
var immed = (typeof sync === 'undefined') ? true : sync;
|
|
// one iteration for each column of the rectangle
|
|
for (var i = x; i < x + w; i += 1) {
|
|
// draws a vert line
|
|
this.drawLine(i, y, i, y + h - 1, color, false);
|
|
}
|
|
if (immed) {
|
|
this._updateDirtyBytes(this.dirtyBytes);
|
|
}
|
|
}
|
|
|
|
// activate scrolling for rows start through stop
|
|
Oled.prototype.startScroll = function (dir, start, stop) {
|
|
var scrollHeader,
|
|
cmdSeq = [];
|
|
|
|
switch (dir) {
|
|
case 'right':
|
|
cmdSeq.push(this.RIGHT_HORIZONTAL_SCROLL); break;
|
|
case 'left':
|
|
cmdSeq.push(this.LEFT_HORIZONTAL_SCROLL); break;
|
|
// TODO: left diag and right diag not working yet
|
|
case 'left diagonal':
|
|
cmdSeq.push(
|
|
this.SET_VERTICAL_SCROLL_AREA, 0x00,
|
|
this.VERTICAL_AND_LEFT_HORIZONTAL_SCROLL,
|
|
this.HEIGHT
|
|
);
|
|
break;
|
|
// TODO: left diag and right diag not working yet
|
|
case 'right diagonal':
|
|
cmdSeq.push(
|
|
this.SET_VERTICAL_SCROLL_AREA, 0x00,
|
|
this.VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL,
|
|
this.HEIGHT
|
|
);
|
|
break;
|
|
}
|
|
|
|
this._waitUntilReady(function () {
|
|
cmdSeq.push(
|
|
0x00, start,
|
|
0x00, stop,
|
|
// TODO: these need to change when diagonal
|
|
0x00, 0xFF,
|
|
this.ACTIVATE_SCROLL
|
|
);
|
|
|
|
var i, cmdSeqLen = cmdSeq.length;
|
|
|
|
for (i = 0; i < cmdSeqLen; i += 1) {
|
|
this._transfer('cmd', cmdSeq[i]);
|
|
}
|
|
}.bind(this));
|
|
}
|
|
|
|
// stop scrolling display contents
|
|
Oled.prototype.stopScroll = function () {
|
|
this._transfer('cmd', this.DEACTIVATE_SCROLL); // stahp
|
|
}
|
|
|
|
/**
|
|
* Draw a circle outline - ported from https://www.npmjs.com/package/oled-ssd1306-i2c/v/1.0.6?activeTab=readme
|
|
* This method is ad verbatim translation from the corresponding
|
|
* method on the Adafruit GFX library
|
|
* https://github.com/adafruit/Adafruit-GFX-Library
|
|
*/
|
|
Oled.prototype.drawCircle = function (x0, y0, r, color, sync) {
|
|
var immed = (typeof sync === 'undefined') ? true : sync;
|
|
|
|
var f = 1 - r;
|
|
var ddF_x = 1;
|
|
var ddF_y = -2 * r;
|
|
var x = 0;
|
|
var y = r;
|
|
|
|
this.drawPixel(
|
|
[[x0, y0 + r, color],
|
|
[x0, y0 - r, color],
|
|
[x0 + r, y0, color],
|
|
[x0 - r, y0, color]],
|
|
false
|
|
);
|
|
|
|
while (x < y) {
|
|
if (f >= 0) {
|
|
y--;
|
|
ddF_y += 2;
|
|
f += ddF_y;
|
|
}
|
|
x++;
|
|
ddF_x += 2;
|
|
f += ddF_x;
|
|
|
|
this.drawPixel(
|
|
[[x0 + x, y0 + y, color],
|
|
[x0 - x, y0 + y, color],
|
|
[x0 + x, y0 - y, color],
|
|
[x0 - x, y0 - y, color],
|
|
[x0 + y, y0 + x, color],
|
|
[x0 - y, y0 + x, color],
|
|
[x0 + y, y0 - x, color],
|
|
[x0 - y, y0 - x, color]],
|
|
false
|
|
);
|
|
}
|
|
|
|
if (immed) {
|
|
this._updateDirtyBytes(this.dirtyBytes);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
module.exports = Oled;
|
|
|
|
/*
|
|
* This code was forked from baltazor's oled-i2c-bus: https://www.npmjs.com/package/oled-i2c-bus
|
|
*/ |