JSFiddle - React, Tailwind, and code Playground

by Bartosz Zieliński

HTML

<canvas width="2000" height="2000" id="c"></canvas>

JavaScript

// =========================================================================
function GRect(x, y, w, h) {
	this._width = w;
  this._height = h;
  this._x = x;
  this._y = y;
}

GRect.prototype._x = 0;
GRect.prototype._y = 0;
GRect.prototype._width = 0;
GRect.prototype._height = 0;
GRect.prototype.getX = function () {
	return this._x;
}
GRect.prototype.getY = function () {
	return this._y;
}
GRect.prototype.getWidth = function() {
	return this._width;
}
GRect.prototype.getHeight = function() {
	return this._height;
}
GRect.prototype.scaled = function (s) {
	return new GRect(this._x*s, this._y*s, this._width*s, this._height*s);
}
GRect.prototype.toString = function () {
return "new GRect(" + this._x + "," + this._y + "," + this._width + "," + this._height + ")";
}

/**
 * A class for merging or subtracting rectangles from it
 * It was born here: https://jsfiddle.net/to267224/0uvfjo4p/121/
 * @class
 * @constructor
 */
function GAABBMerger() {
    this._yStrips = [];
    this._intervals = [];
}

/**
 * @typedef Pair
 * @type {[Number, Number]}
 */

/**
 * @type {Array<Pair>}
 * @private
 */
GAABBMerger.prototype._yStrips = null;

/**
 * @type {Array<Array<Pair>>}
 * @private
 */
GAABBMerger.prototype._intervals = null;

/**
 * Sets whole structure to dirty
 */
GAABBMerger.prototype.reset = function () {
    this._yStrips = [];
    this._intervals = [];
}

/**
 * Subtracts the rectangle from an array of clean rectangles
 * @param {GRect} rect 
 */
GAABBMerger.prototype.subtract = function (rect) {
    if (!this._yStrips.length) {
        return;
    }

    function cpy(arr2d) {
        return arr2d.map(function (item) {
            return [item[0], item[1]];
        });
    }

    var x1 = rect.getX();
    var x2 = rect.getX() + rect.getWidth();
    var y1 = rect.getY();
    var y2 = rect.getY() + rect.getHeight();

    var locationY1 = this._locate(this._yStrips, y1);
    // rectangle falls outside, at the end of all rects
    if (locationY1.idx ===...