class PixelData {
static unpack(array, {height=8, width=8, stacked=false}={}) {
let bit = 0n, idx = 0;
const values = new Uint8Array(height * width);
const unpacked = [ ];
array.forEach((packed, k) => {
idx = 0;
for(let row=0; row<height; ++row) {
for(let col=0; col<width; ++col) {
bit = 1n << BigInt(8 * row + col);
if(!stacked) values[idx] = (packed & bit) ? 1 : 0;
else if(packed & bit) values[idx] = k+1;
idx++;
}
}
if(!stacked) unpacked.push(new PixelData(height, width, values));
});
return stacked ? new PixelData(height, width, values) : unpacked;
}
constructor(height, width, values) {
this.height = height;
this.width = width;
this.values = new Uint8Array(height * width);
if(!(values === undefined) && values.length == height * width) this.set(values);
}
set(values) {
for(let i=0; i < this.height * this.width; ++i) this.values[i] = values[i];
}
at(row, col) {
return this.values[row * this.width + col];
}
squeezed() {
const rowSum = row => d3.range(this.width).reduce((sum,col) => sum + this.at(row,col), 0);
const colSum = col => d3.range(this.height).reduce((sum,row) => sum + this.at(row,col), 0);
let left,right,top,bottom;
for(top=0; top < this.height; ++top) if(rowSum(top)) break;
for(bottom=this.height-1; bottom >= 0; --bottom) if(rowSum(bottom)) break;
for(left=0; left < this.width; ++left) if(colSum(left)) break;
for(right=this.width-1; right >= 0; --right) if(colSum(right)) break;
const height = bottom - top + 1, width = right - left + 1;
if(height == this.height && width == this.width) return this;
const values = new Uint8Array(height * width);
for(let row = top; row <= bottom; ++row) {
for(let col = left; col <= right; ++col) {
values[(row - top) * width + (col - left)] = this.at(row, col);
}
}
return new PixelData(height, width, values);
}
isEqualTo(other) {
if(other.width != this.width || other.height != this.height) return false;
for(let row = 0; row < this.height; ++row) {
for(let col = 0; col < this.width; ++col) {
if(other.at(row,col) != this.at(row,col)) return false;
}
}
return true;
}
pack(array, {idx=0, dx=0, dy=0}) {
if(this.width > 8 || this.height > 8) throw new Error(`Cannot pack ${this.height} x ${this.width} pixels`);
array[idx] = 0n;
let bit = 0n;
for(let row=0; row < this.height; ++row) {
for(let col=0; col < this.width; ++col) {
if(this.at(row, col)) array[idx] |= 1n << BigInt(8 * (row+dy) + col + dx);
}
}
return array;
}
}