/*
* Concrete v3.0.6
* A lightweight Html5 Canvas framework that enables hit detection, layering, multi buffering,
* pixel ratio management, exports, and image downloads
* Release Date: 6-29-2020
* https://github.com/ericdrowell/concrete
* Licensed under the MIT or GPL Version 2 licenses.
*
* Copyright (C) 2020 Eric Rowell @ericdrowell
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
var Concrete = {},
idCounter = 0;
Concrete.PIXEL_RATIO = (function () {
return (window && window.devicePixelRatio) || 1;
})();
Concrete.viewports = [];
////////////////////////////////////////////////////////////// VIEWPORT //////////////////////////////////////////////////////////////
/**
* Concrete Viewport constructor
* @param {Object} config
* @param {Integer} config.width - viewport width in pixels
* @param {Integer} config.height - viewport height in pixels
*/
Concrete.Viewport = function(config) {
if (!config) {
config = {};
}
this.container = config.container;
this.layers = [];
this.id = idCounter++;
this.scene = new Concrete.Scene();
this.setSize(config.width || 0, config.height || 0);
// clear container
config.container.innerHTML = '';
config.container.appendChild(this.scene.canvas);
Concrete.viewports.push(this);
};
Concrete.Viewport.prototype = {
/**
* add layer
* @param {Concrete.Layer} layer
* @returns {Concrete.Viewport}
*/
add: function(layer) {
this.layers.push(layer);
layer.setSize(layer.width || this.width, layer.height || this.height);
layer.viewport = this;
return this;
},
/**
* set viewport size
* @param {Integer} width - viewport width in pixels
* @param {Integer} height - viewport height in pixels
* @returns {Concrete.Viewport}
*/
setSize: function(width, height) {
this.width = width;
this.height = height;
this.scene.setSize(width, height);
this.layers.forEach(function(layer) {
layer.setSize(width, height);
});
return this;
},
/**
* get key associated to coordinate. This can be used for mouse interactivity.
* @param {Number} x
* @param {Number} y
* @returns {Integer} integer - returns -1 if no pixel is there
*/
getIntersection: function(x, y) {
var layers = this.layers,
len = layers.length,
n, layer, key;
for (n=len-1; n>=0; n--) {
layer = layers[n];
key = layer.hit.getIntersection(x, y);
if (key >= 0) {
return key;
}
}
return -1;
},
/**
* get viewport index from all Concrete viewports
* @returns {Integer}
*/
getIndex: function() {
var viewports = Concrete.viewports,
len = viewports.length,
n = 0,
viewport;
for (n=0; n 0) {
// swap
layers[index] = layers[index-1];
layers[index-1] = this;
}
return this;
},
/**
* move to top
* @returns {Concrete.Layer}
*/
moveToTop: function() {
var index = this.getIndex(),
viewport = this.viewport,
layers = viewport.layers;
layers.splice(index, 1);
layers.push(this);
},
/**
* move to bottom
* @returns {Concrete.Layer}
*/
moveToBottom: function() {
var index = this.getIndex(),
viewport = this.viewport,
layers = viewport.layers;
layers.splice(index, 1);
layers.unshift(this);
return this;
},
/**
* get layer index from viewport layers
* @returns {Number|null}
*/
getIndex: function() {
var layers = this.viewport.layers,
len = layers.length,
n = 0,
layer;
for (n=0; n this.width || y > this.height) {
return -1;
}
// 2d
if (this.contextType === '2d') {
data = context.getImageData(x, y, 1, 1).data;
if (data[3] < 255) {
return -1;
}
}
// webgl
else {
data = new Uint8Array(4);
context.readPixels(x*Concrete.PIXEL_RATIO, (this.height - y - 1)*Concrete.PIXEL_RATIO, 1, 1, context.RGBA, context.UNSIGNED_BYTE, data);
if (data[0] === 255 && data[1] === 255 && data[2] === 255) {
return -1;
}
}
return this.rgbToInt(data);
},
/**
* get canvas formatted color string from data index
* @param {Number} index
* @returns {String}
*/
getColorFromIndex: function(index) {
var rgb = this.intToRGB(index);
return 'rgb(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ')';
},
/**
* converts rgb array to integer value
* @param {Array.}
*/
intToRGB: function(number) {
var r = (number & 0xff0000) >> 16;
var g = (number & 0x00ff00) >> 8;
var b = (number & 0x0000ff);
return [r, g, b];
},
};
// export
(function (global) {
'use strict';
// AMD support
if (typeof define === 'function' && define.amd) {
define(function () { return Concrete; });
// CommonJS and Node.js module support.
} else if (typeof exports !== 'undefined') {
// Support Node.js specific `module.exports` (which can be a function)
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = Concrete;
}
// But always support CommonJS module 1.1.1 spec (`exports` cannot be a function)
exports.Concrete = Concrete;
} else {
global.Concrete = Concrete;
}
})(this);