plotboilerplate
Version:
A simple javascript plotting boilerplate for 2d stuff.
759 lines • 26.9 kB
JavaScript
"use strict";
/**
* @author Extended, bugfixed and ported to TypeScript by Ikaros Kappler.
* @modified 2018-xx-xx Added a clone() function.
* @modified 2018-xx-xx Allowing leading '#' in the makeHEX() function.
* @modified 2018-11-28 Fixed the checkHEX() function to accept 000000.
* @modified 2019-11-18 Added a generic parse(string) function that detects the format.
* @modified 2020-01-09 Fixed a bug in the parse(string) function. Hex colors with only three elements were considered faulty.
* @modified 2020-10-23 Ported to Typescript.
* @modified 2021-02-08 Fixed a lot of es2015 compatibility issues.
* @modified 2021-02-08 Added basic tsdoc/jsdoc comments.
* @modified 2021-11-05 Fixing the regex to parse rgba-strings.
* @modified 2021-11-05 Added return value `this` to all modifier functions (for chaining).
* @modified 2021-11-07 Changed the behavior of `darken` and `lighten`: the passed value is handled relative now which makes values much easier predictable and makes the change feel more 'natural'.
* @modified 2021-11-07 Did the same with `saturate` and `desaturate`.
* @modified 2021-11-07 Did the same with the `fadein` and `fadeout` functions.
* @modified 2021-11-07 Added setRed, setGreen, setBlue, setHue, setSaturation, setLiminance functions.
* @modified 2022-05-11 Modified the `clone` function by just copying the numeric calues, not re-calculating the whole color.
* @modified 2022-05-11 Fixed the `interpolate` function.
* @modified 2023-01-23 Added `Color.set(Color)` function to set all values (r,g,b,h,s,l,a) simultanoeusly.
* @modified 2024-03-10 Fixed some NaN type check for Typescript 5 compatibility.
* @version 0.0.13
**/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Color = void 0;
/**
* @classdesc A color class, inspired by neolitec's Javascript class.
* Original found at
* https://gist.github.com/neolitec/1344610
* Thanks to neolitec
*/
var Color = /** @class */ (function () {
/**
* Construct a new color with `r=0 g=0 b=0`.
*
* Consider using the `makeHex`, `makeRGB` or `makeHSL` functions.
*
* @constructor
* @instance
* @memberof Color
*/
function Color() {
this.r = this.g = this.b = 0;
this.h = this.s = this.l = 0;
this.a = 1;
}
// --- RGB ----------------------------------
/**
* Get this color as a CSS `rgb` string.
*
* Consult this for details: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
*
* @method cssRGB
* @instance
* @memberof Color
* @return {string} This color as a CSS rgb string.
*/
Color.prototype.cssRGB = function () {
return "rgb(" + Math.round(255 * this.r) + "," + Math.round(255 * this.g) + "," + Math.round(255 * this.b) + ")";
};
/**
* Get this color as a CSS `rgba` string.
*
* Consult this for details: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
*
* @method cssRGBA
* @instance
* @memberof Color
* @return {string} This color as a CSS rgba string.
*/
Color.prototype.cssRGBA = function () {
return "rgba(".concat(Math.round(255 * this.r), ",").concat(Math.round(255 * this.g), ",").concat(Math.round(255 * this.b), ",").concat(this.a, ")");
};
/**
* Get the red component of this RGB(A)color. This method just returns the `r` color attribute.
*
* @method red
* @instance
* @memberof Color
* @return {number} A value between 0.0 and 1.0.
*/
Color.prototype.red = function () {
return this.r;
};
/**
* Get the green component of this RGB(A) color. This method just returns the `g` color attribute.
*
* @method green
* @instance
* @memberof Color
* @return {number} A value between 0.0 and 1.0.
*/
Color.prototype.green = function () {
return this.g;
};
/**
* Get the blue component of this RGB(A) color. This method just returns the `b` color attribute.
*
* @method blue
* @instance
* @memberof Color
* @return {number} A value between 0.0 and 1.0.
*/
Color.prototype.blue = function () {
return this.b;
};
Color.prototype.set = function (color) {
this.r = color.r;
this.g = color.g;
this.b = color.b;
this.a = color.a;
this.h = color.h;
this.s = color.s;
this.l = color.l;
return this;
};
Color.prototype.setRed = function (r) {
this.r = r;
return this;
};
Color.prototype.setBlue = function (b) {
this.b = b;
Color.Converter.RGBToHSL(this);
return this;
};
Color.prototype.setAlpha = function (a) {
this.a = a;
Color.Converter.RGBToHSL(this);
return this;
};
Color.prototype.setGreen = function (g) {
this.g = g;
Color.Converter.RGBToHSL(this);
return this;
};
Color.prototype.setHue = function (h) {
this.h = h;
Color.Converter.HSLToRGB(this);
return this;
};
Color.prototype.setSaturation = function (s) {
this.s = s;
Color.Converter.HSLToRGB(this);
return this;
};
Color.prototype.setLuminance = function (l) {
this.l = l;
Color.Converter.HSLToRGB(this);
return this;
};
// --- HSL ----------------------------------
/**
* Get this color as a CSS `hsl` string.
*
* @method cssHSL
* @instance
* @memberof Color
* @return {string} This color as a CSS hsl string.
*/
Color.prototype.cssHSL = function () {
return "hsl(" + Math.round(360 * this.h) + "," + Math.round(100 * this.s) + "%," + Math.round(100 * this.l) + "%)";
};
/**
* Get this color as a CSS `hsla` string.
*
* @method cssHSLA
* @instance
* @memberof Color
* @return {string} This color as a CSS hsla string.
*/
Color.prototype.cssHSLA = function () {
return ("hsla(" +
Math.round(360 * this.h) +
"," +
Math.round(100 * this.s) +
"%," +
Math.round(100 * this.l) +
"%," +
Math.round(this.a) +
")");
};
/**
* Get the hue component of this HSL(A) color. This method just returns the `h` color attribute.
*
* @method hue
* @instance
* @memberof Color
* @return {number} A value between 0.0 and 1.0.
*/
Color.prototype.hue = function () {
return this.h;
};
/**
* Get the saturation component of this HSL(A) color. This method just returns the `s` color attribute.
*
* @method saturation
* @instance
* @memberof Color
* @return {number} A value between 0.0 and 1.0.
*/
Color.prototype.saturation = function () {
return this.s;
};
/**
* Get the lightness component of this HSL(A) color. This method just returns the `l` color attribute.
*
* @method lightness
* @instance
* @memberof Color
* @return {number} A value between 0.0 and 1.0.
*/
Color.prototype.lightness = function () {
return this.l;
};
// --- HEX ----------------------------------
/**
* Get this color as a CSS-HEX string (non-alpha): #rrggbb
*
* @method cssHEX
* @instance
* @memberof Color
* @return {string} This color as a CSS-HEX string.
*/
Color.prototype.cssHEX = function () {
return ("#" +
(255 * this.r < 16 ? "0" : "") +
Math.round(255 * this.r).toString(16) +
(255 * this.g < 16 ? "0" : "") +
Math.round(255 * this.g).toString(16) +
(255 * this.b < 16 ? "0" : "") +
Math.round(255 * this.b).toString(16));
};
// --- Transparency ----------------------------------
/**
* Get the alpha channel (transparency) of this color.
*
* @method alpha
* @instance
* @memberof Color
* @return {number} A value between 0.0 and 1.0.
*/
Color.prototype.alpha = function () {
return this.a;
};
// --- Modifiers ----------------------------------
// saturate(v: string | number): Color {
// if ("string" == typeof v && v.indexOf("%") > -1 && (v = parseInt(v)) != NaN) {
// this.s += v / 100;
// } else if ("number" == typeof v) {
// // range 255
// this.s += v / 255;
// } else {
// throw new Error("error: bad modifier format (percent or number)");
// }
// if (this.s > 1) this.s = 1;
// else if (this.s < 0) this.s = 0;
// Color.Converter.HSLToRGB(this);
// return this;
// }
Color.prototype.saturate = function (v) {
// if ("string" == typeof v && v.indexOf("%") > -1 && (v = parseInt(v)) != NaN) {
if ("string" == typeof v && v.indexOf("%") > -1 && !Number.isNaN(v = parseInt(v))) {
this.s += (1 - this.s) * (v / 100);
}
else if ("number" == typeof v) {
if (v >= -0.0 && v <= 1.0) {
// range 255
this.s += (1 - this.s) * v;
}
else {
// range 0-1
this.s += (1 - this.s) * (v / 255);
}
}
else {
throw new Error("error: bad modifier format (percent or number)");
}
if (this.s > 1)
this.s = 1;
else if (this.s < 0)
this.s = 0;
Color.Converter.HSLToRGB(this);
return this;
};
Color.prototype.desaturate = function (v) {
// if ("string" == typeof v && v.indexOf("%") > -1 && (v = parseInt(v)) != NaN) {
if ("string" == typeof v && v.indexOf("%") > -1 && !Number.isNaN(v = parseInt(v))) {
this.s -= v / 100;
}
else if ("number" == typeof v) {
if (v >= 0.0 && v <= 1.0) {
// range 255
this.s -= this.s * v;
}
else {
// range 0-1
this.s -= this.s * (v / 255);
}
}
else {
throw new Error("error: bad modifier format (percent or number)");
}
if (this.s > 1)
this.s = 1;
else if (this.s < 0)
this.s = 0;
Color.Converter.HSLToRGB(this);
return this;
};
// lighten(v: string | number): Color {
// if ("string" == typeof v && v.indexOf("%") > -1 && (v = parseInt(v)) != NaN) {
// this.l += v / 100;
// } else if ("number" == typeof v) {
// if (v >= -1.0 && v <= 1.0) {
// // range 0.0...1.0
// this.l += v;
// } else {
// // range 255
// this.l += v / 255;
// }
// } else {
// throw new Error("error: bad modifier format (percent or number)");
// }
// if (this.l > 1) this.l = 1;
// else if (this.l < 0) this.l = 0;
// Color.Converter.HSLToRGB(this);
// return this;
// }
Color.prototype.lighten = function (v) {
// if ("string" == typeof v && v.indexOf("%") > -1 && (v = parseInt(v)) != NaN) {
if ("string" == typeof v && v.indexOf("%") > -1 && !Number.isNaN(v = parseInt(v))) {
this.l += (1 - this.l) * (v / 100);
}
else if ("number" == typeof v) {
if (v >= 0.0 && v <= 1.0) {
// range 0.0...1.0
this.l += (1 - this.l) * v;
}
else {
// range 255
this.l += (1 - this.l) * (v / 255);
}
}
else {
throw new Error("error: bad modifier format (percent or number)");
}
if (this.l > 1)
this.l = 1;
else if (this.l < 0)
this.l = 0;
Color.Converter.HSLToRGB(this);
return this;
};
Color.prototype.darken = function (v) {
// if ("string" == typeof v && v.indexOf("%") > -1 && (v = parseInt(v)) != NaN) {
if ("string" == typeof v && v.indexOf("%") > -1 && !Number.isNaN(v = parseInt(v))) {
this.l -= this.l * (v / 100);
}
else if ("number" == typeof v) {
if (v >= 0.0 && v <= 1.0) {
// range 0.0...1.0
this.l -= this.l * v;
}
else {
// range 255
this.l -= this.l * (v / 255);
}
}
else {
throw new Error("error: bad modifier format (percent or number)");
}
if (this.l > 1)
this.l = 1;
else if (this.l < 0)
this.l = 0;
Color.Converter.HSLToRGB(this);
return this;
};
Color.prototype.fadein = function (v) {
// if ("string" == typeof v && v.indexOf("%") > -1 && (v = parseInt(v)) != NaN) {
if ("string" == typeof v && v.indexOf("%") > -1 && !Number.isNaN(v = parseInt(v))) {
this.a += (1 - this.a) * (v / 100);
}
else if ("number" == typeof v) {
if (v >= 0.0 && v <= 1.0) {
// range 0-1
this.a += (1 - this.a) * v;
}
else {
// range 255
this.a += (1 - this.a) * (v / 255);
}
}
else {
throw new Error("error: bad modifier format (percent or number)");
}
console.log("New alpha", this.a);
if (this.a > 1)
this.a = 1;
else if (this.a < 0)
this.a = 0;
Color.Converter.HSLToRGB(this);
return this;
};
Color.prototype.fadeout = function (v) {
// if ("string" == typeof v && v.indexOf("%") > -1 && (v = parseInt(v)) != NaN) {
if ("string" == typeof v && v.indexOf("%") > -1 && !Number.isNaN(v = parseInt(v))) {
this.a -= v / 100;
}
else if ("number" == typeof v) {
if (v >= 0.0 && v <= 1.0) {
// range 0-1
this.a -= v;
}
else {
// range 255
this.a -= v / 255;
}
}
else {
throw new Error("error: bad modifier format (percent or number)");
}
if (this.a > 1)
this.a = 1;
else if (this.a < 0)
this.a = 0;
Color.Converter.HSLToRGB(this);
return this;
};
Color.prototype.spin = function (v) {
// if ("string" == typeof v && v.indexOf("%") > -1 && (v = parseInt(v)) != NaN) this.h += v / 100;
if ("string" == typeof v && v.indexOf("%") > -1 && !Number.isNaN(v = parseInt(v))) {
this.h += v / 100;
}
else if ("number" == typeof v) {
// range 360
this.h += v / 360;
}
else
throw new Error("error: bad modifier format (percent or number)");
if (this.h > 1)
this.h = 1;
else if (this.h < 0)
this.h = 0;
Color.Converter.HSLToRGB(this);
return this;
};
Color.makeRGB = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var c = new Color();
var sanitized;
if (arguments.length < 3 || arguments.length > 4)
throw new Error("error: 3 or 4 arguments");
sanitized = Color.Sanitizer.RGB(arguments[0], arguments[1], arguments[2]);
c.r = sanitized[0];
c.g = sanitized[1];
c.b = sanitized[2];
if (arguments.length == 4) {
c.a = arguments[3];
}
else {
c.a = 1.0;
}
Color.Converter.RGBToHSL(c);
return c;
};
Color.makeHSL = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var c = new Color();
var sanitized;
if (arguments.length < 3 || arguments.length > 4)
throw new Error("error: 3 or 4 arguments");
sanitized = Color.Sanitizer.HSL(arguments[0], arguments[1], arguments[2]);
c.h = sanitized[0];
c.s = sanitized[1];
c.l = sanitized[2];
if (arguments.length == 4)
c.a = arguments[3];
else
c.a = 1.0;
Color.Converter.HSLToRGB(c);
return c;
};
Color.makeHEX = function (value) {
var c = new Color(), sanitized;
// Edit Ika 2018-0308
// Allow leading '#'
if (value && value.startsWith("#"))
value = value.substr(1);
Color.Validator.checkHEX(value);
if (value.length == 3) {
sanitized = Color.Sanitizer.RGB(parseInt(value.substr(0, 1) + value.substr(0, 1), 16), parseInt(value.substr(1, 1) + value.substr(1, 1), 16), parseInt(value.substr(2, 1) + value.substr(2, 1), 16));
}
else if (value.length == 6) {
sanitized = Color.Sanitizer.RGB(parseInt(value.substr(0, 2), 16), parseInt(value.substr(2, 2), 16), parseInt(value.substr(4, 2), 16));
}
else
throw new Error("error: 3 or 6 arguments");
c.r = sanitized[0];
c.g = sanitized[1];
c.b = sanitized[2];
c.a = 1.0; // TODO: Accept #xxxxxxxx (8 chars, too, for alpha)
Color.Converter.RGBToHSL(c);
return c;
};
/**
* Parse the given color string. Currently only these formate are recognized: hex, rgb, rgba.
*
* @method parse
* @static
* @memberof Color
* @param {string} str - The string representation to parse.
* @return {Color} The color instance that's represented by the given string.
*/
Color.parse = function (str) {
if (typeof str == "undefined") {
return null;
}
if ((str = str.trim().toLowerCase()).length == 0) {
return null;
}
if (str.startsWith("#"))
return Color.makeHEX(str.substring(1, str.length));
if (str.startsWith("rgb")) {
var parts = str.match(/^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,?\s*(\d*(?:\.\d+\s*)?)\)$/);
if (!parts) {
throw "Unrecognized color format (2): " + str;
}
// [ str, r, g, b, a|undefined ]
// console.log("parts", parts);
if (parts.length <= 4 || typeof parts[4] == "undefined" || parts[4] == "") {
return Color.makeRGB(parts[1], parts[2], parts[3]);
}
else {
return Color.makeRGB(parts[1], parts[2], parts[3], Number(parts[4]));
}
}
else {
throw "Unrecognized color format (1): " + str;
}
};
/**
* Create a clone of this color (RGB).
*
* @method clone
* @instance
* @memberof Color
* @return {Color} A clone of this color (in RGB mode).
*/
Color.prototype.clone = function () {
// return Color.makeRGB(this.r, this.g, this.b, this.a);
var col = new Color();
col.r = this.r;
col.g = this.g;
col.b = this.b;
col.a = this.a;
col.h = this.h;
col.s = this.s;
col.l = this.l;
return col;
};
/**
* Interpolate this color on the RGB scale.
*
* @method interpolate
* @instance
* @memberof Color
* @param {Color} c - The color to interpolate to.
* @param {number} t - An interpolation value between 0.0 and 1.0.
* @return {Color} A clone of this color (in RGB mode).
*/
Color.prototype.interpolate = function (c, t) {
this.r += (c.r - this.r) * t;
this.g += (c.g - this.g) * t;
this.b += (c.b - this.b) * t;
this.a += (c.a - this.a) * t;
return this;
};
Color.Sanitizer = {
RGB: function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var o = [];
if (arguments.length == 0) {
return [];
}
// const allAreFrac = Color.testFrac( arguments );
for (var i = 0; i < arguments.length; i++) {
var c = arguments[i];
if ("string" == typeof c && c.indexOf("%") > -1) {
// if ((c = parseInt(c)) == NaN) throw new Error("Bad format");
if (Number.isNaN(c = parseInt(c))) {
throw new Error("Bad format");
}
if (c < 0 || c > 100)
throw new Error("Bad format");
o[i] = c / 100;
}
else {
// if ("string" == typeof c && (c = parseInt(c)) == NaN) {
if ("string" == typeof c && Number.isNaN(c = parseInt(c))) {
throw new Error("Bad format");
}
if (c < 0) {
throw new Error("Bad format");
}
//else if( allAreFrac ) o[i] = c; // c >= 0 && c <= 1 (all)
else if (c >= 0 && c < 1) {
o[i] = c;
}
// else if(c >= 0.0 && c <= 1.0) o[i] = c;
else if (c >= 1 && c < 256) {
o[i] = c / 255;
}
// ???
// else if(c >= 0 && c < 256) o[i] = c/255;
else
throw new Error("Bad format (" + c + ")");
}
}
return o;
},
HSL: function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
if (arguments.length < 3 || arguments.length > 4)
throw new Error("3 or 4 arguments required");
var h = arguments[0], s = arguments[1], l = arguments[2];
// if ("string" == typeof h && (h = parseFloat(h)) == NaN) throw new Error("Bad format for hue");
if ("string" == typeof h && Number.isNaN(h = parseFloat(h))) {
throw new Error("Bad format for hue");
}
if (h < 0 || h > 360)
throw new Error("Hue out of range (0..360)");
else if ((("" + h).indexOf(".") > -1 && h > 1) || ("" + h).indexOf(".") == -1)
h /= 360;
if ("string" == typeof s && s.indexOf("%") > -1) {
// if ((s = parseInt(s)) == NaN) throw new Error("Bad format for saturation");
if (Number.isNaN(s = parseInt(s))) {
throw new Error("Bad format for saturation");
}
if (s < 0 || s > 100)
throw new Error("Bad format for saturation");
s /= 100;
}
else if (s < 0 || s > 1)
throw new Error("Bad format for saturation");
if ("string" == typeof l && l.indexOf("%") > -1) {
// if ((l = parseInt(l)) == NaN) throw new Error("Bad format for lightness");
if (Number.isNaN(l = parseInt(l))) {
throw new Error("Bad format for lightness");
}
if (l < 0 || l > 100)
throw new Error("Bad format for lightness");
l /= 100;
}
else if (l < 0 || l > 1)
throw new Error("Bad format for lightness");
return [h, s, l];
}
}; // ENd sanitizer
Color.Validator = {
/**
* Check a hexa color (without #)
*/
checkHEX: function (value) {
if (value.length != 6 && value.length != 3)
throw new Error("Hexa color: bad length (" + value.length + ")," + value);
value = value.toLowerCase();
//for( var i in value ) {
for (var i = 0; i < value.length; i++) {
var c = value.charCodeAt(i);
if (!((c >= 48 && c <= 57) || (c >= 97 && c <= 102)))
throw new Error("Hexa color: out of range for \"".concat(value, "\" at position ").concat(i, "."));
}
}
};
Color.Converter = {
/**
* Calculates HSL Color.
* RGB must be normalized.
* http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
*/
RGBToHSL: function (color) {
var r = color.r;
var g = color.g;
var b = color.b;
var max = Math.max(r, g, b);
var min = Math.min(r, g, b);
color.l = (max + min) / 2;
if (max == min) {
color.h = color.s = 0; // achromatic
}
else {
var d = max - min;
color.s = color.l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
color.h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
color.h = (b - r) / d + 2;
break;
case b:
color.h = (r - g) / d + 4;
break;
}
color.h /= 6;
}
},
/**
* Calculates RGB color (nomalized).
* HSL must be normalized.
*
* http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
*/
HSLToRGB: function (color) {
var h = color.h;
var s = color.s;
var l = color.l;
if (s == 0) {
color.r = color.g = color.b = l; // achromatic
}
else {
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
color.r = Color.Converter.hue2rgb(p, q, h + 1 / 3);
color.g = Color.Converter.hue2rgb(p, q, h);
color.b = Color.Converter.hue2rgb(p, q, h - 1 / 3);
}
},
hue2rgb: function (p, q, t) {
if (t < 0)
t += 1;
if (t > 1)
t -= 1;
if (t < 1 / 6)
return p + (q - p) * 6 * t;
if (t < 1 / 2)
return q;
if (t < 2 / 3)
return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
};
return Color;
}()); // END class
exports.Color = Color;
//# sourceMappingURL=Color.js.map